From bf95a1cf63c63655baf6b5b4c379506288798d41 Mon Sep 17 00:00:00 2001 From: Daniela Soares Feitosa Date: Wed, 28 Oct 2009 12:56:28 -0300 Subject: [PATCH] Communities can be moderated by environment admin --- app/controllers/my_profile/memberships_controller.rb | 24 ++++++++++-------------- app/controllers/public/account_controller.rb | 2 +- app/models/community.rb | 17 ++++++++++++++++- app/models/create_community.rb | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/models/environment.rb | 5 +++++ app/models/profile.rb | 4 ++-- app/models/task_mailer.rb | 4 +++- app/views/task_mailer/target_notification.rhtml | 2 +- app/views/tasks/_create_community.rhtml | 34 ++++++++++++++++++++++++++++++++++ features/create_community.feature | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ features/step_definitions/create_community_steps.rb | 22 ++++++++++++++++++++++ features/step_definitions/noosfero_steps.rb | 27 +++++++++++++++++++++++++++ test/fixtures/roles.yml | 7 +++++++ test/functional/account_controller_test.rb | 15 +++++++++++++++ test/functional/memberships_controller_test.rb | 7 +++++++ test/unit/community_test.rb | 42 ++++++++++++++++++++++++++++++++++++++++++ test/unit/create_community_test.rb | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/unit/profile_test.rb | 13 +++++++++++++ 18 files changed, 458 insertions(+), 20 deletions(-) create mode 100644 app/models/create_community.rb create mode 100644 app/views/tasks/_create_community.rhtml create mode 100644 features/create_community.feature create mode 100644 features/step_definitions/create_community_steps.rb create mode 100644 test/unit/create_community_test.rb diff --git a/app/controllers/my_profile/memberships_controller.rb b/app/controllers/my_profile/memberships_controller.rb index 904534d..fbcb3e6 100644 --- a/app/controllers/my_profile/memberships_controller.rb +++ b/app/controllers/my_profile/memberships_controller.rb @@ -7,21 +7,17 @@ class MembershipsController < MyProfileController end def new_community - community_data = environment.enabled?('organizations_are_moderated_by_default') ? { :moderated_articles => true } : {} - community_data.merge!(params[:community]) if params[:community] - @community = Community.new(community_data) - @community.environment = environment @wizard = params[:wizard].blank? ? false : params[:wizard] - if request.post? - if @community.save - @community.add_admin(profile) - if @wizard - redirect_to :controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true - return - else - redirect_to :action => 'index' - return - end + @community = Community.new(params[:community]) + @community.environment = environment + if request.post? && @community.valid? + @community = Community.create_after_moderation(user, {:environment => environment}.merge(params[:community])) + if @wizard + redirect_to :controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true + return + else + redirect_to :action => 'index' + return end end if @wizard diff --git a/app/controllers/public/account_controller.rb b/app/controllers/public/account_controller.rb index 6f3a5da..54e49d9 100644 --- a/app/controllers/public/account_controller.rb +++ b/app/controllers/public/account_controller.rb @@ -221,7 +221,7 @@ class AccountController < ApplicationController def check_url @identifier = params[:identifier] - valid = Person.is_available?(@identifier) + valid = Person.is_available?(@identifier, environment) if valid @status = _('Available!') @status_class = 'available' diff --git a/app/models/community.rb b/app/models/community.rb index dc8949a..cf46a00 100644 --- a/app/models/community.rb +++ b/app/models/community.rb @@ -7,6 +7,21 @@ class Community < Organization xss_terminate :only => [ :name, :address, :contact_phone, :description ] + before_create do |community| + community.moderated_articles = true if community.environment.enabled?('organizations_are_moderated_by_default') + end + + def self.create_after_moderation(requestor, attributes = {}) + community = Community.new(attributes) + if community.environment.enabled?('admin_must_approve_new_communities') + CreateCommunity.create(attributes.merge(:requestor => requestor)) + else + community = Community.create(attributes) + community.add_admin(requestor) + end + community + end + FIELDS = %w[ description language @@ -20,7 +35,7 @@ class Community < Organization super self.required_fields.each do |field| if self.send(field).blank? - self.errors.add(field, _('%{fn} is mandatory')) + self.errors.add(field, _('%{fn} is mandatory')) end end end diff --git a/app/models/create_community.rb b/app/models/create_community.rb new file mode 100644 index 0000000..63f4ad7 --- /dev/null +++ b/app/models/create_community.rb @@ -0,0 +1,89 @@ +class CreateCommunity < Task + + validates_presence_of :requestor_id, :target_id + validates_presence_of :name + + alias :environment :target + alias :environment= :target= + + serialize :data, Hash + attr_protected :data + def data + self[:data] ||= Hash.new + end + + DATA_FIELDS = Community.fields + ['name', 'closed', 'image_builder', 'tag_list'] + + DATA_FIELDS.each do |field| + # getter + define_method(field) do + self.data[field.to_sym] + end + # setter + define_method("#{field}=") do |value| + self.data[field.to_sym] = value + end + end + + def validate + self.environment.required_community_fields.each do |field| + if self.send(field).blank? + self.errors.add(field, _('%{fn} is mandatory')) + end + end + end + + def perform + community = Community.new + community_data = self.data.reject do |key, value| + ! DATA_FIELDS.include?(key.to_s) + end + + community.update_attributes(community_data) + community.environment = self.environment + community.save! + community.add_admin(self.requestor) + end + + def description + _('%s wants to create community %s.') % [requestor.name, self.name] + end + + def closing_statement + data[:closing_statement] + end + + def closing_statement= value + data[:closing_statement] = value + end + + # tells if this request was rejected + def rejected? + self.status == Task::Status::CANCELLED + end + + # tells if this request was appoved + def approved? + self.status == Task::Status::FINISHED + end + + def target_notification_message + description + "\n\n" + + _("User \"%{user}\" just requested to create community %{community}. You have to approve or reject it through the \"Pending Validations\" section in your control panel.\n") % { :user => self.requestor.name, :community => self.name } + end + + def task_created_message + _("Your request for registering community %{community} at %{environment} was just sent. Environment administrator will receive it and will approve or reject your request according to his methods and creteria. + + You will be notified as soon as environment administrator has a position about your request.") % { :community => self.name, :environment => self.target } + end + + def task_cancelled_message + _("Your request for registering community %{community} at %{environment} was not approved by the environment administrator. The following explanation was given: \n\n%{explanation}") % { :community => self.name, :environment => self.environment, :explanation => self.closing_statement } + end + + def task_finished_message + _('Your request for registering the community "%{community}" was approved. You can access %{environment} now and start using your new community.') % { :community => self.name, :environment => self.environment } + end + +end diff --git a/app/models/environment.rb b/app/models/environment.rb index af77512..b49c2f3 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -130,6 +130,7 @@ class Environment < ActiveRecord::Base 'articles_dont_accept_comments_by_default' => _("Articles don't accept comments by default"), 'organizations_are_moderated_by_default' => _("Organizations have moderated publication by default"), 'enable_organization_url_change' => _("Allow organizations to change their address"), + 'admin_must_approve_new_communities' => _("Admin must approve creation of communities"), } end @@ -662,6 +663,10 @@ class Environment < ActiveRecord::Base "home-page-news/#{cache_key}" end + def notification_emails + [contact_email.blank? ? nil : contact_email].compact + admins.map(&:email) + end + after_create :create_templates def create_templates diff --git a/app/models/profile.rb b/app/models/profile.rb index d094b05..566c9c8 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -198,8 +198,8 @@ class Profile < ActiveRecord::Base @top_level_articles ||= Article.top_level_for(self) end - def self.is_available?(identifier) - !(identifier =~ IDENTIFIER_FORMAT).nil? && !RESERVED_IDENTIFIERS.include?(identifier) && Profile.find(:first, :conditions => ['environment_id = ? and identifier = ?', Environment.default.id, identifier]).nil? + def self.is_available?(identifier, environment) + !(identifier =~ IDENTIFIER_FORMAT).nil? && !RESERVED_IDENTIFIERS.include?(identifier) && Profile.find(:first, :conditions => ['environment_id = ? and identifier = ?', environment.id, identifier]).nil? end validates_presence_of :identifier, :name diff --git a/app/models/task_mailer.rb b/app/models/task_mailer.rb index 7d26e5e..d159da1 100644 --- a/app/models/task_mailer.rb +++ b/app/models/task_mailer.rb @@ -14,6 +14,8 @@ class TaskMailer < ActionMailer::Base recipients task.target.notification_emails + url_for_tasks_list = task.target.kind_of?(Environment) ? '' : url_for(task.target.url.merge(:controller => 'tasks', :action => 'index')) + from self.class.generate_from(task) subject '[%s] %s' % [task.requestor.environment.name, task.description] body :requestor => task.requestor.name, @@ -21,7 +23,7 @@ class TaskMailer < ActionMailer::Base :message => msg, :environment => task.requestor.environment.name, :url => url_for(:host => task.requestor.environment.default_hostname, :controller => 'home'), - :tasks_url => url_for(task.target.url.merge(:controller => 'tasks', :action => 'index')) + :tasks_url => url_for_tasks_list end def invitation_notification(task) diff --git a/app/views/task_mailer/target_notification.rhtml b/app/views/task_mailer/target_notification.rhtml index 19ea23f..7d334ea 100644 --- a/app/views/task_mailer/target_notification.rhtml +++ b/app/views/task_mailer/target_notification.rhtml @@ -2,7 +2,7 @@ <%= word_wrap(@message) %> -<%= word_wrap(_('Access the address below to see this and other pending actions that need your attention:')) %> +<%= word_wrap(_('Access your list of tasks or your control panel to see this and other pending actions that need your attention.')) %> <%= @tasks_url %> -- diff --git a/app/views/tasks/_create_community.rhtml b/app/views/tasks/_create_community.rhtml new file mode 100644 index 0000000..f6150f5 --- /dev/null +++ b/app/views/tasks/_create_community.rhtml @@ -0,0 +1,34 @@ +

<%= _('New community') %>

+ +<%= link_to( profile_image(task.requestor, :minor, :border => 0), task.requestor.public_profile_url ) %> + +<%= _('%s wants to create community %s.') % + [content_tag('strong', link_to( task.requestor.name, task.requestor.public_profile_url ) ), + content_tag('strong', task.name )] %> + +<% form_for('task', task, :url => { :action => 'close', :id => task.id } ) do |f| %> + +
+ <% if !Profile.is_available?(task.name.to_slug, environment) %> +

<%= _('This name was already taken, this community cannot be approved') %>

+ <%= hidden_field_tag(:decision, 'cancel') %> + <% else %> + <%= radio_button_tag(:decision, 'finish', true, + :id => "decision-finish-#{task.id}") %> + + +     + + <%= radio_button_tag(:decision, 'cancel', false, + :id => "decision-cancel-#{task.id}") %> + + <% end %> + + <%= labelled_form_field _('Please provide an explanation for the rejection'), f.text_area(:closing_statement, :style => 'height:200px; width:80%;') %> + +
+ + <% button_bar do %> + <%= submit_button(:ok, _('Ok!')) %> + <% end %> +<% end %> diff --git a/features/create_community.feature b/features/create_community.feature new file mode 100644 index 0000000..66e0674 --- /dev/null +++ b/features/create_community.feature @@ -0,0 +1,96 @@ +Feature: create community + As a noosfero user + I want to create a community + In order to interact with other people + + Background: + Given the following users + | login | name | + | joaosilva | Joao Silva | + + Scenario: a user creates a community + Given I am logged in as "joaosilva" + And feature "admin_must_approve_new_communities" is disabled on environment + And I follow "Control panel" + And I follow "Manage my groups" + When I follow "Create a new community" + And I fill in "Name" with "Fancy community" + And I press "Create" + Then I should see "Fancy community" + + Scenario: a user creates a community when environment moderates it + Given I am logged in as "joaosilva" + And feature "admin_must_approve_new_communities" is enabled on environment + When I follow "Control panel" + And I follow "Manage my groups" + And I follow "Create a new community" + And I fill in "Name" with "Community for moderation" + And I press "Create" + Then I should not see "Community for moderation" + + Scenario: a user tries to create a community without a name + Given I am logged in as "joaosilva" + And feature "admin_must_approve_new_communities" is disabled on environment + And I follow "Control panel" + And I follow "Manage my groups" + When I follow "Create a new community" + And I press "Create" + Then I should see "Creating new community" + + Scenario: environment admin receive a task when a user creates a community + Given I am logged in as admin + And feature "admin_must_approve_new_communities" is enabled on environment + When I create community "Community for approval" + And I follow "Control Panel" + Then I should see "admin_user wants to create community Community for approval" + + Scenario: environment admin accepts new community task + Given I am logged in as admin + And feature "admin_must_approve_new_communities" is enabled on environment + When I create community "Community for approval" + And I follow "Control Panel" + And I follow "Process requests" + And I should see "admin_user wants to create community Community for approval" + And I choose "Approve" + When I press "Ok!" + Then I should not see "admin_user wants to create community Community for approval" + When I follow "My groups" + Then I should see "Community for approval" + + Scenario: environment admin rejects new community task + Given I am logged in as admin + And feature "admin_must_approve_new_communities" is enabled on environment + When I create community "Community for approval" + And I follow "Control Panel" + And I follow "Process requests" + And I should see "admin_user wants to create community Community for approval" + And I choose "Reject" + When I press "Ok!" + Then I should not see "admin_user wants to create community Community for approval" + When I follow "My groups" + Then I should not see "Community for approval" + + Scenario: new community is listed after approval + Given I am logged in as admin + And feature "admin_must_approve_new_communities" is enabled on environment + When I create community "Community for approval" + And I approve community "Community for approval" + When I follow "My groups" + Then I should see "Community for approval" + + Scenario: new community is not listed after rejection + Given I am logged in as admin + And feature "admin_must_approve_new_communities" is enabled on environment + When I create community "Community for approval" + And I reject community "Community for approval" + When I follow "My groups" + Then I should not see "Community for approval" + + Scenario: environment admin accepts new community task but identifier was already taken + Given I am logged in as admin + And feature "admin_must_approve_new_communities" is enabled on environment + And I create community "Community for approval" + And I create community "Community for approval" + When I approve community "Community for approval" + Then I should see "This name was already taken, this community cannot be approved" + And I should see "admin_user wants to create community Community for approval" diff --git a/features/step_definitions/create_community_steps.rb b/features/step_definitions/create_community_steps.rb new file mode 100644 index 0000000..8143a51 --- /dev/null +++ b/features/step_definitions/create_community_steps.rb @@ -0,0 +1,22 @@ +Given /^I create community "(.+)"$/ do |community| + click_link('My groups') + click_link('Create a new community') + fill_in("Name", :with => community) + click_button("Create") +end + +Given /^I approve community "(.+)"$/ do |community| + task = CreateCommunity.all.select {|c| c.name == community}.first + click_link('Control Panel') + click_link('Process requests') + choose("decision-finish-#{task.id}") + click_button('OK!') +end + +Given /^I reject community "(.+)"$/ do |community| + task = CreateCommunity.all.select {|c| c.name == community}.first + click_link('Control Panel') + click_link('Process requests') + choose("decision-cancel-#{task.id}") + click_button('OK!') +end diff --git a/features/step_definitions/noosfero_steps.rb b/features/step_definitions/noosfero_steps.rb index f5ae66b..e04bd89 100644 --- a/features/step_definitions/noosfero_steps.rb +++ b/features/step_definitions/noosfero_steps.rb @@ -62,7 +62,34 @@ Given /^the following products$/ do |table| end Given /^I am logged in as "(.+)"$/ do |username| + visit('/account/login') fill_in("Username", :with => username) fill_in("Password", :with => '123456') click_button("Log in") end + +Given /^I am logged in as admin$/ do + user = User.create!(:login => 'admin_user', :password => '123456', :password_confirmation => '123456', :email => 'admin_user@example.com') + e = Environment.default + e.add_admin(user.person) + visit('/account/login') + fill_in("Username", :with => user.login) + fill_in("Password", :with => '123456') + click_button("Log in") +end + +Given /^I am not logged in$/ do |username| + visit('/account/logout') +end + +Given /^feature "(.+)" is enabled on environment$/ do |feature| + e = Environment.default + e.enable(feature) + e.save +end + +Given /^feature "(.+)" is disabled on environment$/ do |feature| + e = Environment.default + e.disable(feature) + e.save +end diff --git a/test/fixtures/roles.yml b/test/fixtures/roles.yml index 8efed65..fdb2bb5 100644 --- a/test/fixtures/roles.yml +++ b/test/fixtures/roles.yml @@ -73,3 +73,10 @@ environment_administrator: system: true permissions: - perform_task + - view_environment_admin_panel + - edit_environment_features + - edit_environment_design + - manage_environment_categories + - manage_environment_roles + - manage_environment_validators + - moderate_comments diff --git a/test/functional/account_controller_test.rb b/test/functional/account_controller_test.rb index c47d588..ace9ee9 100644 --- a/test/functional/account_controller_test.rb +++ b/test/functional/account_controller_test.rb @@ -649,6 +649,21 @@ class AccountControllerTest < Test::Unit::TestCase assert_redirected_to :controller => 'home', :action => 'index' end + should 'check_url is available on environment' do + env = Environment.create(:name => 'Environment test') + @controller.expects(:environment).returns(env).at_least_once + profile = create_user('mylogin').person + get :check_url, :identifier => 'mylogin' + assert_equal 'available', assigns(:status_class) + end + + should 'check if url is not available on environment' do + @controller.expects(:environment).returns(Environment.default).at_least_once + profile = create_user('mylogin').person + get :check_url, :identifier => 'mylogin' + assert_equal 'unavailable', assigns(:status_class) + end + protected def new_user(options = {}, extra_options ={}) data = {:profile_data => person_data} diff --git a/test/functional/memberships_controller_test.rb b/test/functional/memberships_controller_test.rb index 154abf9..fa5087d 100644 --- a/test/functional/memberships_controller_test.rb +++ b/test/functional/memberships_controller_test.rb @@ -220,6 +220,13 @@ class MembershipsControllerTest < Test::Unit::TestCase get :new_community, :profile => profile.identifier assert_not_nil assigns(:community).environment + end + + should 'set environment' do + @controller.stubs(:environment).returns(Environment.default).at_least_once + post :new_community, :profile => profile.identifier, :community => {:name => 'test community'} + + assert_not_nil assigns(:community).environment end should 'not show description if isnt enabled when register new community' do diff --git a/test/unit/community_test.rb b/test/unit/community_test.rb index 6d4fd22..c9bfd52 100644 --- a/test/unit/community_test.rb +++ b/test/unit/community_test.rb @@ -2,6 +2,12 @@ require File.dirname(__FILE__) + '/../test_helper' class CommunityTest < Test::Unit::TestCase + def setup + @person = create_user('testuser').person + end + + attr_reader :person + should 'inherit from Profile' do assert_kind_of Profile, Community.new end @@ -144,5 +150,41 @@ class CommunityTest < Test::Unit::TestCase assert_equal [highlighted_t].map(&:slug), c.news(2, true).map(&:slug) end + should 'sanitize description' do + c = Community.create!(:name => 'test_com', :description => 'new community') + + assert_sanitized c.description + end + + should 'sanitize name' do + c = Community.create!(:name => 'test_com') + + assert_sanitized c.name + end + should 'create a task when creating a community if feature is enabled' do + env = Environment.default + env.enable('admin_must_approve_new_communities') + + assert_difference CreateCommunity, :count do + Community.create_after_moderation(person, {:environment => env, :name => 'Example'}) + end + + assert_no_difference Community, :count do + Community.create_after_moderation(person, {:environment => env, :name => 'Example'}) + end + end + + should 'create a community if feature is disabled' do + env = Environment.default + env.disable('admin_must_approve_new_communities') + + assert_difference Community, :count do + Community.create_after_moderation(person, {:environment => env, :name => 'Example'}) + end + + assert_no_difference CreateCommunity, :count do + Community.create_after_moderation(person, {:environment => env, :name => 'Example'}) + end + end end diff --git a/test/unit/create_community_test.rb b/test/unit/create_community_test.rb new file mode 100644 index 0000000..c60ed42 --- /dev/null +++ b/test/unit/create_community_test.rb @@ -0,0 +1,68 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class CreateCommunityTest < Test::Unit::TestCase + + def setup + @person = create_user('testing').person + end + attr_reader :person + + should 'provide needed data' do + task = CreateCommunity.new + + Community.fields + %w[ name closed image_builder tag_list ].each do |field| + assert task.respond_to?(field) + assert task.respond_to?("#{field}=") + end + end + + should 'require a requestor' do + task = CreateCommunity.new(:name => 'community test', :target => Environment.default) + task.valid? + + assert task.errors.invalid?(:requestor_id) + task.requestor = person + task.valid? + assert !task.errors.invalid?(:requestor_id) + end + + should 'actually create a community when finishing the task and associate the task requestor as its admin' do + + task = CreateCommunity.create!({ + :name => 'My new community', + :requestor => person, + :target => Environment.default, + }) + + assert_difference Community, :count do + task.finish + end + + assert_equal person, Community['my-new-community'].admins.first + end + + should 'override message methods from Task' do + specific = CreateCommunity.new + %w[ task_created_message task_finished_message task_cancelled_message ].each do |method| + assert_nothing_raised NotImplementedError do + specific.send(method) + end + end + end + + should 'provide a message to be sent to the target' do + assert_not_nil CreateCommunity.new(:name => 'test comm', :requestor => person).target_notification_message + end + + should 'report as approved when approved' do + request = CreateCommunity.new + request.stubs(:status).returns(Task::Status::FINISHED) + assert request.approved? + end + + should 'report as rejected when rejected' do + request = CreateCommunity.new + request.stubs(:status).returns(Task::Status::CANCELLED) + assert request.rejected? + end +end diff --git a/test/unit/profile_test.rb b/test/unit/profile_test.rb index 5f82d00..c48536d 100644 --- a/test/unit/profile_test.rb +++ b/test/unit/profile_test.rb @@ -1462,6 +1462,19 @@ class ProfileTest < Test::Unit::TestCase assert_equal [event2, event3, event1], profile.events end + should 'be available if identifier doesnt exist on environment' do + p = create_user('identifier-test').person + + env = Environment.create(:name => 'Environment test') + assert_equal true, Profile.is_available?('identifier-test', env) + end + + should 'not be available if identifier exists on environment' do + p = create_user('identifier-test').person + + assert_equal false, Profile.is_available?('identifier-test', Environment.default) + end + private def assert_invalid_identifier(id) -- libgit2 0.21.2