[Git][noosfero/noosfero][master] 3 commits: Added filter section on profile members list
Rodrigo Souto
gitlab at mg.gitlab.com
Thu Mar 3 15:18:40 BRT 2016
Rodrigo Souto pushed to branch master at Noosfero / noosfero
Commits:
80ebad1f by Michel Felipe de Oliveira Ferreira at 2016-02-26T11:56:53-03:00
Added filter section on profile members list
- Adds use of filtered members to mailing queue executed by send_mail action
Signed-off-by: Gustavo Jaruga <darksshades at gmail.com>
Signed-off-by: Marcos Ronaldo <marcos.rpj2 at gmail.com>
Signed-off-by: Michel Felipe de Oliveira Ferreira <michel.ferreira at serpro.gov.br>
- - - - -
3cdf7126 by Marcos Ronaldo at 2016-03-03T15:15:36-03:00
adds member selection to organization mailing
Signed-off-by: Gustavo Jaruga <darkshades at gmail.com>
Signed-off-by: Marcos Ronaldo <marcos.rpj2 at gmail.com>
- - - - -
be66917b by Rodrigo Souto at 2016-03-03T18:18:23+00:00
Merge branch 'master_profile_members_filter' into 'master'
Added filter section on profile members list
- Adds use of filtered members to mailing queue executed by send_mail action
Signed-off-by: Gustavo Jaruga <darksshades at gmail.com>
Signed-off-by: Marcos Ronaldo <marcos.rpj2 at gmail.com>
Signed-off-by: Michel Felipe de Oliveira Ferreira <michel.ferreira at serpro.gov.br>
See merge request !800
- - - - -
21 changed files:
- app/controllers/my_profile/profile_members_controller.rb
- app/controllers/public/profile_controller.rb
- app/helpers/forms_helper.rb
- app/mailers/mailing.rb
- app/mailers/organization_mailing.rb
- app/models/person.rb
- app/models/profile.rb
- app/views/profile/send_mail.html.erb
- app/views/profile_members/_index_buttons.html.erb
- + app/views/profile_members/_members_filter.erb
- app/views/profile_members/_members_list.html.erb
- app/views/profile_members/index.html.erb
- + db/migrate/20160224132937_add_data_to_mailing.rb
- db/schema.rb
- features/send_email_to_organization_members.feature
- public/designs/themes/noosfero/style.css
- + public/javascripts/profile_members.js
- test/functional/profile_controller_test.rb
- test/functional/profile_members_controller_test.rb
- test/unit/organization_mailing_test.rb
- test/unit/profile_test.rb
Changes:
=====================================
app/controllers/my_profile/profile_members_controller.rb
=====================================
--- a/app/controllers/my_profile/profile_members_controller.rb
+++ b/app/controllers/my_profile/profile_members_controller.rb
@@ -2,8 +2,26 @@ class ProfileMembersController < MyProfileController
protect 'manage_memberships', :profile
def index
- @members = profile.members_by_name
- @member_role = environment.roles.find_by_name('member')
+ @filters = params[:filters] || {:roles => []}
+ @filters[:roles] = [] unless @filters[:roles]
+ @data = {}
+ field = 'name'
+ field = 'email' if @filters[:name] =~ /\@/
+
+ @data[:members] = profile.members_by(field, at filters[:name]).by_role(@filters[:roles])
+ session[:members_filtered] = @data[:members].map{|m|m.id} if request.post?
+ @data[:roles] = Profile::Roles.organization_member_roles(environment.id) | Profile::Roles.organization_custom_roles(environment.id, profile.id)
+
+ end
+
+ def send_mail
+ session[:members_filtered] = params[:members_filtered].select{|value| value!="0"}
+ if session[:members_filtered].present?
+ redirect_to :controller => :profile, :action => :send_mail
+ else
+ session[:notice] = _("Select at least one member.")
+ redirect_to :action => :index
+ end
end
def update_roles
@@ -156,4 +174,13 @@ class ProfileMembersController < MyProfileController
end
end
+ def search_members
+ field = 'name'
+ field = 'email' if params[:filter_name] =~ /\@/
+
+ result = profile.members_like field, params[:filter_name]
+ result = result.select{|member| member.can_view_field?(current_person, "email") } if field=="email"
+ render :json => result.map { |member| {:label => "#{member.name}#{member.can_view_field?(current_person, "email") ? " <#{member.email}>" : ""}", :value => member.name }}
+ end
+
end
=====================================
app/controllers/public/profile_controller.rb
=====================================
--- a/app/controllers/public/profile_controller.rb
+++ b/app/controllers/public/profile_controller.rb
@@ -370,6 +370,7 @@ class ProfileController < PublicController
def send_mail
@mailing = profile.mailings.build(params[:mailing])
+ @mailing.data = session[:members_filtered] ? {:members_filtered => session[:members_filtered]} : {}
if request.post?
@mailing.locale = locale
@mailing.person = user
=====================================
app/helpers/forms_helper.rb
=====================================
--- a/app/helpers/forms_helper.rb
+++ b/app/helpers/forms_helper.rb
@@ -7,9 +7,10 @@ module FormsHelper
def labelled_check_box( human_name, name, value = "1", checked = false, options = {} )
options[:id] ||= 'checkbox-' + FormsHelper.next_id_number
- hidden_field_tag(name, '0') +
- check_box_tag( name, value, checked, options ) +
- content_tag( 'label', human_name, :for => options[:id] )
+ html = options[:add_hidden] == false ? "" : hidden_field_tag(name, '0')
+
+ html += check_box_tag( name, value, checked, options ) +
+ content_tag( 'label', human_name, :for => options[:id] )
end
def labelled_text_field( human_name, name, value=nil, options={} )
=====================================
app/mailers/mailing.rb
=====================================
--- a/app/mailers/mailing.rb
+++ b/app/mailers/mailing.rb
@@ -2,7 +2,10 @@ require_dependency 'mailing_job'
class Mailing < ActiveRecord::Base
- attr_accessible :subject, :body
+ acts_as_having_settings :field => :data
+
+ attr_accessible :subject, :body, :data
+
validates_presence_of :source_id, :subject, :body
belongs_to :source, :foreign_key => :source_id, :polymorphic => true
belongs_to :person
=====================================
app/mailers/organization_mailing.rb
=====================================
--- a/app/mailers/organization_mailing.rb
+++ b/app/mailers/organization_mailing.rb
@@ -5,9 +5,17 @@ class OrganizationMailing < Mailing
end
def recipients(offset=0, limit=100)
- source.members.order(:id).offset(offset).limit(limit)
- .joins("LEFT OUTER JOIN mailing_sents m ON (m.mailing_id = #{id} AND m.person_id = profiles.id)")
+ result = source.members.order(:id).offset(offset).limit(limit)
+
+ if data.present? and data.is_a?(Hash) and data[:members_filtered]
+ result = result.where('profiles.id IN (?)', data[:members_filtered])
+ end
+
+ if result.blank?
+ result = result.joins("LEFT OUTER JOIN mailing_sents m ON (m.mailing_id = #{id} AND m.person_id = profiles.id)")
.where("m.person_id" => nil)
+ end
+ result
end
def each_recipient
=====================================
app/models/person.rb
=====================================
--- a/app/models/person.rb
+++ b/app/models/person.rb
@@ -16,10 +16,13 @@ class Person < Profile
acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)}
acts_as_accessor
- scope :members_of, -> resources {
+ scope :members_of, lambda { |resources, field = ''|
resources = Array(resources)
+ joins = [:role_assignments]
+ joins << :user if User.attribute_names.include? field
+
conditions = resources.map {|resource| "role_assignments.resource_type = '#{resource.class.base_class.name}' AND role_assignments.resource_id = #{resource.id || -1}"}.join(' OR ')
- distinct.select('profiles.*').joins(:role_assignments).where([conditions])
+ select('DISTINCT profiles.*').joins(joins).where([conditions])
}
scope :not_members_of, -> resources {
@@ -48,6 +51,14 @@ class Person < Profile
['( roles.key = ? AND role_assignments.accessor_type = ? AND role_assignments.accessor_id = ? ) OR (
( ( friendships.person_id = ? ) OR (profiles.public_profile = ?)) AND (profiles.visible = ?) )', 'environment_administrator', Profile.name, person.id, person.id, true, true]
).uniq
+ }
+ scope :by_role, lambda { |roles|
+
+ roles = [roles] unless roles.kind_of?(Array)
+
+ if roles.length > 0
+ {:select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.role_id IN (?)', roles] }
+ end
}
=====================================
app/models/profile.rb
=====================================
--- a/app/models/profile.rb
+++ b/app/models/profile.rb
@@ -49,6 +49,9 @@ class Profile < ActiveRecord::Base
def self.organization_member_roles(env_id)
all_roles(env_id).select{ |r| r.key.match(/^profile_/) unless r.key.blank? || !r.profile_id.nil?}
end
+ def self.organization_custom_roles(env_id, profile_id)
+ all_roles(env_id).where('profile_id = ?', profile_id)
+ end
def self.all_roles(env_id)
Role.where(environment_id: env_id)
end
@@ -155,15 +158,23 @@ class Profile < ActiveRecord::Base
include TimeScopes
- def members
+ def members(by_field = '')
scopes = plugins.dispatch_scopes(:organization_members, self)
- scopes << Person.members_of(self)
+ scopes << Person.members_of(self,by_field)
return scopes.first if scopes.size == 1
ScopeTool.union *scopes
end
- def members_by_name
- members.order('profiles.name')
+ def members_by(field,value = nil)
+ if value and !value.blank?
+ members_like(field,value).order('profiles.name')
+ else
+ members.order('profiles.name')
+ end
+ end
+
+ def members_like(field,value)
+ members(field).where("LOWER(#{field}) LIKE ?", "%#{value.downcase}%") if value
end
class << self
@@ -1098,6 +1109,10 @@ private :generate_url, :url_options
end
end
+ def can_view_field? current_person, field
+ display_private_info_to?(current_person) || (public_fields.include?(field) && public?)
+ end
+
validates_inclusion_of :redirection_after_login, :in => Environment.login_redirection_options.keys, :allow_nil => true
def preferred_login_redirection
redirection_after_login.blank? ? environment.redirection_after_login : redirection_after_login
=====================================
app/views/profile/send_mail.html.erb
=====================================
--- a/app/views/profile/send_mail.html.erb
+++ b/app/views/profile/send_mail.html.erb
@@ -4,6 +4,9 @@
<%= error_messages_for :mailing %>
+ <% to = @mailing.data[:members_filtered].present? ? @mailing.recipients.map{|r| r.name}.join(', ') : _('All members')%>
+ <%= labelled_form_field(_('To:'), text_area(:data, 'members_filtered', :value => to, :rows => 4, :disabled => 'disabled', :class => 'send-mail-recipients')) %>
+
<%= form_for :mailing, :url => {:action => 'send_mail'}, :html => {:id => 'mailing-form'} do |f| %>
<%= labelled_form_field(_('Subject:'), f.text_field(:subject)) %>
=====================================
app/views/profile_members/_index_buttons.html.erb
=====================================
--- a/app/views/profile_members/_index_buttons.html.erb
+++ b/app/views/profile_members/_index_buttons.html.erb
@@ -5,7 +5,7 @@
<%= button :person, _('Invite people to join'), :controller => 'invite', :action => 'invite_friends' %>
<% end %>
<% if profile.community? and user.has_permission?(:send_mail_to_members, profile) %>
- <%= button :send, _('Send e-mail to members'), :controller => 'profile', :action => 'send_mail' %>
+ <%= submit_button(:send, _('Send e-mail to members')) %>
<% end %>
<% @plugins.dispatch(:manage_members_extra_buttons).each do |plugin_button| %>
<%= button plugin_button[:icon], plugin_button[:title], plugin_button[:url] %>
=====================================
app/views/profile_members/_members_filter.erb
=====================================
--- /dev/null
+++ b/app/views/profile_members/_members_filter.erb
@@ -0,0 +1,18 @@
+<%= form_tag '#', :method => 'post' do %>
+
+ <%= field_set_tag _('Filter'), :class => 'filter_fields' do %>
+ <p>
+ <%= labelled_text_field(_('Name or Email')+': ', "filters[name]", @filters[:name], {:id => 'filter-name-autocomplete',:size => 30}) %>
+ </p>
+
+ <p><%= _('Roles:') %> </p>
+ <% @data[:roles].each do |r| %>
+ <%= labelled_check_box(r.name, 'filters[roles][]', r.id, @filters[:roles].include?(r.id.to_s), :add_hidden => false) %><br/>
+ <% end %>
+ <p>
+ <%= submit_button(:search, _('Search')) %>
+ </p>
+ <% end %>
+<% end %>
+
+<%= javascript_include_tag params[:controller] %>
\ No newline at end of file
=====================================
app/views/profile_members/_members_list.html.erb
=====================================
--- a/app/views/profile_members/_members_list.html.erb
+++ b/app/views/profile_members/_members_list.html.erb
@@ -1,16 +1,22 @@
-<% collection = @collection == :profile_admins ? profile.admins : profile.members_by_name %>
+<% members = @data ? @data[:members] : profile.members_by('name') %>
+<% collection = @collection == :profile_admins ? profile.admins : members %>
<% title = @title ? @title : _('Current members') %>
<% remove_action = @remove_action ? @remove_action : {:action => 'unassociate'} %>
+<%= javascript_include_tag params[:controller] %>
<h3><%= title %></h3>
<table>
+ <col width="1">
<tr>
+ <th><%= check_box_tag 'checkbox-all', 1, false, :onClick => "toggle(this)" %></th>
<th><%= _('Member') %></th>
<th><%= _('Actions') %></th>
</tr>
+
<% collection.each do |m| %>
<tr title="<%= m.name %>">
+ <td><%= labelled_check_box('', 'members_filtered[]', m.id.to_s, false, :id => 'checkbox-'+m.identifier) %></td>
<td><%= link_to_profile m.short_name, m.identifier, :title => m.name %> </td>
<td>
<div class="members-buttons-cell">
@@ -26,3 +32,8 @@
</tr>
<% end %>
</table>
+<% if collection.empty? %>
+ <p>
+ <em><%= _('No members found to: %s') % profile.name %></em>
+ </p>
+<% end %>
=====================================
app/views/profile_members/index.html.erb
=====================================
--- a/app/views/profile_members/index.html.erb
+++ b/app/views/profile_members/index.html.erb
@@ -1,9 +1,12 @@
<h1><%= h profile.short_name(50) %></h1>
-<%= render :partial => 'index_buttons' %>
+<%= render :partial => 'members_filter' %>
-<div id="members-list">
- <%= render :partial => 'members_list' %>
-</div>
+<%= form_tag 'profile_members/send_mail', :method => 'post' do %>
+ <div id="members-list">
+ <%= render :partial => 'members_list' %>
+ </div>
-<%= render :partial => 'index_buttons' %>
+ <%= render :partial => 'index_buttons' %>
+
+<% end %>
=====================================
db/migrate/20160224132937_add_data_to_mailing.rb
=====================================
--- /dev/null
+++ b/db/migrate/20160224132937_add_data_to_mailing.rb
@@ -0,0 +1,5 @@
+class AddDataToMailing < ActiveRecord::Migration
+ def change
+ add_column :mailings, :data, :text
+ end
+end
=====================================
db/schema.rb
=====================================
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20160202142247) do
+ActiveRecord::Schema.define(version: 20160224132937) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -471,6 +471,7 @@ ActiveRecord::Schema.define(version: 20160202142247) do
t.string "locale"
t.datetime "created_at"
t.datetime "updated_at"
+ t.text "data"
end
create_table "national_region_types", force: :cascade do |t|
=====================================
features/send_email_to_organization_members.feature
=====================================
--- a/features/send_email_to_organization_members.feature
+++ b/features/send_email_to_organization_members.feature
@@ -31,7 +31,8 @@ Feature: send emails to organization members
Scenario: Send e-mail to members
Given I am logged in as "joaosilva"
And I go to Sample Community's members management
- And I follow "Send e-mail to members"
+ And I check "checkbox-manoel"
+ And I press "Send e-mail to members"
And I fill in "Subject" with "Hello, member!"
And I fill in "Body" with "We have some news"
When I press "Send"
@@ -40,7 +41,8 @@ Feature: send emails to organization members
Scenario: Not send e-mail to members if subject is blank
Given I am logged in as "joaosilva"
And I go to Sample Community's members management
- And I follow "Send e-mail to members"
+ And I check "checkbox-manoel"
+ And I press "Send e-mail to members"
And I fill in "Body" with "We have some news"
When I press "Send"
Then I should be on /profile/sample-community/send_mail
@@ -48,7 +50,8 @@ Feature: send emails to organization members
Scenario: Not send e-mail to members if body is blank
Given I am logged in as "joaosilva"
And I go to Sample Community's members management
- And I follow "Send e-mail to members"
+ And I check "checkbox-manoel"
+ And I press "Send e-mail to members"
And I fill in "Subject" with "Hello, user!"
When I press "Send"
Then I should be on /profile/sample-community/send_mail
@@ -56,7 +59,8 @@ Feature: send emails to organization members
Scenario: Cancel creation of mailing
Given I am logged in as "joaosilva"
And I go to Sample Community's members management
- And I follow "Send e-mail to members"
+ And I check "checkbox-manoel"
+ And I press "Send e-mail to members"
When I follow "Cancel e-mail"
Then I should be on Sample Community's members management
=====================================
public/designs/themes/noosfero/style.css
=====================================
--- a/public/designs/themes/noosfero/style.css
+++ b/public/designs/themes/noosfero/style.css
@@ -48,3 +48,9 @@
width: 80px;
}
+.action-profile-send_mail .send-mail-recipients {
+ color: #888888;
+ padding: 10px;
+ width: 475px;
+ line-height: 15px;
+}
=====================================
public/javascripts/profile_members.js
=====================================
--- /dev/null
+++ b/public/javascripts/profile_members.js
@@ -0,0 +1,25 @@
+(function($) {
+
+ //Autocomplete to list members
+ $('#filter-name-autocomplete').autocomplete({
+ minLength:2,
+ source:function(request,response){
+ $.ajax({
+ url:document.location.pathname+'/search_members',
+ dataType:'json',
+ data:{
+ filter_name:request.term
+ },
+ success:response
+ });
+ }
+ });
+})(jQuery);
+
+
+function toggle(source) {
+ checkboxes = document.getElementsByName('members_filtered[]');
+ for(var i=0, n=checkboxes.length;i<n;i++) {
+ checkboxes[i].checked = source.checked;
+ }
+}
=====================================
test/functional/profile_controller_test.rb
=====================================
--- a/test/functional/profile_controller_test.rb
+++ b/test/functional/profile_controller_test.rb
@@ -1465,11 +1465,41 @@ class ProfileControllerTest < ActionController::TestCase
create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
login_as('profile_moderator_user')
@controller.stubs(:locale).returns('pt')
+
assert_difference 'Delayed::Job.count', 1 do
post :send_mail, :profile => community.identifier, :mailing => {:subject => 'Hello', :body => 'We have some news'}
end
end
+ should 'send to members_filtered if available' do
+ community = fast_create(Community)
+ create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
+ person = create_user('Any').person
+ community.add_member(person)
+ community.save!
+ login_as('profile_moderator_user')
+
+ post :send_mail, :profile => community.identifier, :mailing => {:subject => 'Hello', :body => 'We have some news'}
+ assert_equivalent community.members, OrganizationMailing.last.recipients
+
+ @request.session[:members_filtered] = [person.id]
+ post :send_mail, :profile => community.identifier, :mailing => {:subject => 'RUN!!', :body => 'Run to the hills!!'}
+ assert_equal [person], OrganizationMailing.last.recipients
+ end
+
+ should 'send email to all members if there is no valid member in members_filtered' do
+ community = fast_create(Community)
+ create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
+ person = create_user('Any').person
+ community.add_member(person)
+ community.save!
+ login_as('profile_moderator_user')
+
+ @request.session[:members_filtered] = [Profile.last.id+1]
+ post :send_mail, :profile => community.identifier, :mailing => {:subject => 'RUN!!', :body => 'Run to the hills!!'}
+ assert_empty OrganizationMailing.last.recipients
+ end
+
should 'save mailing' do
community = fast_create(Community)
create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
=====================================
test/functional/profile_members_controller_test.rb
=====================================
--- a/test/functional/profile_members_controller_test.rb
+++ b/test/functional/profile_members_controller_test.rb
@@ -31,6 +31,31 @@ class ProfileMembersControllerTest < ActionController::TestCase
assert_template 'index'
end
+ should 'access index and filter members by name and roles' do
+
+ ent = fast_create(Enterprise, :identifier => 'test_enterprise', :name => 'test enterprise')
+ roles = {
+ :admin => Profile::Roles.admin(Environment.default),
+ :member => Profile::Roles.member(Environment.default)
+ }
+
+ member = create_user('test_member', :email => 'testmember at test.com.br').person
+ member.add_role(roles[:member], ent)
+
+ admin = create_user('test_admin').person
+ admin.add_role roles[:admin], ent
+
+ user = create_user_with_permission('test_user', 'manage_memberships', ent)
+ login_as :test_user
+
+ post :index, :profile => 'test_enterprise' , :filters => {:name => 'testmember at test.com.br', :roles => [roles[:member].id]}
+
+ assert_response :success
+ assert_template 'index'
+
+ assert_includes assigns(:data)[:members], member
+ end
+
should 'show form to change role' do
ent = fast_create(Enterprise, :identifier => 'test_enterprise', :name => 'test enterprise')
role = Profile::Roles.member(Environment.default)
@@ -171,7 +196,7 @@ class ProfileMembersControllerTest < ActionController::TestCase
login_as :test_user
get :index, :profile => community.identifier
- assert_tag :tag => 'a', :attributes => {:href => /send_mail/}
+ assert_tag :tag => 'input', :attributes => {:value => 'Send e-mail to members'}
end
should 'not display send email to members if doesn\'t have the permission' do
=====================================
test/unit/organization_mailing_test.rb
=====================================
--- a/test/unit/organization_mailing_test.rb
+++ b/test/unit/organization_mailing_test.rb
@@ -98,6 +98,11 @@ class OrganizationMailingTest < ActiveSupport::TestCase
assert_equal [Person['user_one'], Person['user_two']], mailing.recipients
end
+ should 'return recipients previously filtered' do
+ mailing = create(OrganizationMailing, :source => community, :subject => 'Hello', :body => 'We have some news', :person => person, :data => {:members_filtered => [Person['user_one'].id,Person['user_two'].id]})
+ assert_equivalent [Person['user_one'], Person['user_two']], mailing.recipients
+ end
+
should 'return recipients according to limit' do
mailing = create(OrganizationMailing, :source => community, :subject => 'Hello', :body => 'We have some news', :person => person)
assert_equal [Person['user_one']], mailing.recipients(0, 1)
=====================================
test/unit/profile_test.rb
=====================================
--- a/test/unit/profile_test.rb
+++ b/test/unit/profile_test.rb
@@ -1816,6 +1816,21 @@ class ProfileTest < ActiveSupport::TestCase
assert_equal [person], community.members
end
+ should 'return a list members by email of a community' do
+ someone = create_user('Someone', email:'someone at test.com.br')
+ someperson = create_user('Someperson',email:'someperson at test.com.br')
+
+ community = fast_create(Community)
+ community.add_member(someone.person)
+ community.add_member(someperson.person)
+
+ result = community.members_like 'email', '@test.com.br'
+
+ assert_includes result, someone.person
+ assert_includes result, someperson.person
+
+ end
+
should 'count unique members of a community' do
person = fast_create(Person)
community = fast_create(Community)
View it on GitLab: https://gitlab.com/noosfero/noosfero/compare/f2aaabd6dd85b2e436d734c6420ae91e4599c347...be66917b09dd234c35e2ed493bcf4bc63089627b
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://listas.softwarelivre.org/pipermail/noosfero-dev/attachments/20160303/8937a98d/attachment-0001.html>
More information about the Noosfero-dev
mailing list