Commit 80ebad1ff0bf5f7384a2048ba1b4ba60f847fad3

Authored by Michel Felipe
Committed by Marcos Pereira
1 parent ea20a3dc

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@gmail.com>
Signed-off-by: Marcos Ronaldo <marcos.rpj2@gmail.com>
Signed-off-by: Michel Felipe de Oliveira Ferreira <michel.ferreira@serpro.gov.br>
app/controllers/my_profile/profile_members_controller.rb
... ... @@ -2,8 +2,16 @@ class ProfileMembersController &lt; MyProfileController
2 2 protect 'manage_memberships', :profile
3 3  
4 4 def index
5   - @members = profile.members_by_name
6   - @member_role = environment.roles.find_by_name('member')
  5 + @filters = params[:filters] || {:roles => []}
  6 + @filters[:roles] = [] unless @filters[:roles]
  7 + @data = {}
  8 + field = 'name'
  9 + field = 'email' if @filters[:name] =~ /\@/
  10 +
  11 + @data[:members] = profile.members_by(field,@filters[:name]).by_role(@filters[:roles])
  12 + session[:members_filtered] = @data[:members].map{|m|m.id} if request.post?
  13 + @data[:roles] = Profile::Roles.organization_member_roles(environment.id)
  14 +
7 15 end
8 16  
9 17 def update_roles
... ... @@ -156,4 +164,13 @@ class ProfileMembersController &lt; MyProfileController
156 164 end
157 165 end
158 166  
  167 + def search_members
  168 + field = 'name'
  169 + field = 'email' if params[:filter_name] =~ /\@/
  170 +
  171 + result = profile.members_like field, params[:filter_name]
  172 + result = result.select{|member| member.can_view_field?(current_person, "email") } if field=="email"
  173 + render :json => result.map { |member| {:label => "#{member.name}#{member.can_view_field?(current_person, "email") ? " <#{member.email}>" : ""}", :value => member.name }}
  174 + end
  175 +
159 176 end
... ...
app/controllers/public/profile_controller.rb
... ... @@ -370,6 +370,7 @@ class ProfileController &lt; PublicController
370 370  
371 371 def send_mail
372 372 @mailing = profile.mailings.build(params[:mailing])
  373 + @mailing.data = session[:members_filtered] ? {:members_filtered => session[:members_filtered]} : {}
373 374 if request.post?
374 375 @mailing.locale = locale
375 376 @mailing.person = user
... ...
app/helpers/forms_helper.rb
... ... @@ -7,9 +7,10 @@ module FormsHelper
7 7  
8 8 def labelled_check_box( human_name, name, value = "1", checked = false, options = {} )
9 9 options[:id] ||= 'checkbox-' + FormsHelper.next_id_number
10   - hidden_field_tag(name, '0') +
11   - check_box_tag( name, value, checked, options ) +
12   - content_tag( 'label', human_name, :for => options[:id] )
  10 + html = options[:add_hidden] == false ? "" : hidden_field_tag(name, '0')
  11 +
  12 + html += check_box_tag( name, value, checked, options ) +
  13 + content_tag( 'label', human_name, :for => options[:id] )
13 14 end
14 15  
15 16 def labelled_text_field( human_name, name, value=nil, options={} )
... ...
app/mailers/mailing.rb
... ... @@ -2,7 +2,10 @@ require_dependency &#39;mailing_job&#39;
2 2  
3 3 class Mailing < ActiveRecord::Base
4 4  
5   - attr_accessible :subject, :body
  5 + acts_as_having_settings :field => :data
  6 +
  7 + attr_accessible :subject, :body, :data
  8 +
6 9 validates_presence_of :source_id, :subject, :body
7 10 belongs_to :source, :foreign_key => :source_id, :polymorphic => true
8 11 belongs_to :person
... ...
app/mailers/organization_mailing.rb
... ... @@ -5,9 +5,16 @@ class OrganizationMailing &lt; Mailing
5 5 end
6 6  
7 7 def recipients(offset=0, limit=100)
8   - source.members.order(:id).offset(offset).limit(limit)
  8 + if data.present? and data.is_a?(Hash) and data[:members_filtered]
  9 + result = source.members.where('profiles.id IN (?)', data[:members_filtered])
  10 + end
  11 +
  12 + if result.blank?
  13 + result = source.members.order(:id).offset(offset).limit(limit)
9 14 .joins("LEFT OUTER JOIN mailing_sents m ON (m.mailing_id = #{id} AND m.person_id = profiles.id)")
10 15 .where("m.person_id" => nil)
  16 + end
  17 + result
11 18 end
12 19  
13 20 def each_recipient
... ...
app/models/person.rb
... ... @@ -16,10 +16,13 @@ class Person &lt; Profile
16 16 acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)}
17 17 acts_as_accessor
18 18  
19   - scope :members_of, -> resources {
  19 + scope :members_of, lambda { |resources, field = ''|
20 20 resources = Array(resources)
  21 + joins = [:role_assignments]
  22 + joins << :user if User.attribute_names.include? field
  23 +
21 24 conditions = resources.map {|resource| "role_assignments.resource_type = '#{resource.class.base_class.name}' AND role_assignments.resource_id = #{resource.id || -1}"}.join(' OR ')
22   - distinct.select('profiles.*').joins(:role_assignments).where([conditions])
  25 + select('DISTINCT profiles.*').joins(joins).where([conditions])
23 26 }
24 27  
25 28 scope :not_members_of, -> resources {
... ... @@ -48,6 +51,14 @@ class Person &lt; Profile
48 51 ['( roles.key = ? AND role_assignments.accessor_type = ? AND role_assignments.accessor_id = ? ) OR (
49 52 ( ( friendships.person_id = ? ) OR (profiles.public_profile = ?)) AND (profiles.visible = ?) )', 'environment_administrator', Profile.name, person.id, person.id, true, true]
50 53 ).uniq
  54 + }
  55 + scope :by_role, lambda { |roles|
  56 +
  57 + roles = [roles] unless roles.kind_of?(Array)
  58 +
  59 + if roles.length > 0
  60 + {:select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.role_id IN (?)', roles] }
  61 + end
51 62 }
52 63  
53 64  
... ...
app/models/profile.rb
... ... @@ -155,15 +155,23 @@ class Profile &lt; ActiveRecord::Base
155 155  
156 156 include TimeScopes
157 157  
158   - def members
  158 + def members(by_field = '')
159 159 scopes = plugins.dispatch_scopes(:organization_members, self)
160   - scopes << Person.members_of(self)
  160 + scopes << Person.members_of(self,by_field)
161 161 return scopes.first if scopes.size == 1
162 162 ScopeTool.union *scopes
163 163 end
164 164  
165   - def members_by_name
166   - members.order('profiles.name')
  165 + def members_by(field,value = nil)
  166 + if value and !value.blank?
  167 + members_like(field,value).order('profiles.name')
  168 + else
  169 + members.order('profiles.name')
  170 + end
  171 + end
  172 +
  173 + def members_like(field,value)
  174 + members(field).where("LOWER(#{field}) LIKE ?", "%#{value.downcase}%") if value
167 175 end
168 176  
169 177 class << self
... ... @@ -1098,6 +1106,10 @@ private :generate_url, :url_options
1098 1106 end
1099 1107 end
1100 1108  
  1109 + def can_view_field? current_person, field
  1110 + display_private_info_to?(current_person) || (public_fields.include?(field) && public?)
  1111 + end
  1112 +
1101 1113 validates_inclusion_of :redirection_after_login, :in => Environment.login_redirection_options.keys, :allow_nil => true
1102 1114 def preferred_login_redirection
1103 1115 redirection_after_login.blank? ? environment.redirection_after_login : redirection_after_login
... ...
app/views/profile/send_mail.html.erb
... ... @@ -4,6 +4,9 @@
4 4  
5 5 <%= error_messages_for :mailing %>
6 6  
  7 + <% to = @mailing.data[:members_filtered].present? ? @mailing.recipients.map{|r| r.name}.join(', ') : _('All members')%>
  8 + <%= labelled_form_field(_('To:'), text_area(:data, 'members_filtered', :value => to, :rows => 4, :disabled => 'disabled', :class => 'send-mail-recipients')) %>
  9 +
7 10 <%= form_for :mailing, :url => {:action => 'send_mail'}, :html => {:id => 'mailing-form'} do |f| %>
8 11  
9 12 <%= labelled_form_field(_('Subject:'), f.text_field(:subject)) %>
... ...
app/views/profile_members/_members_filter.erb 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +<%= form_tag '#', :method => 'post' do %>
  2 +
  3 + <%= field_set_tag _('Filter'), :class => 'filter_fields' do %>
  4 + <p>
  5 + <%= labelled_text_field(_('Name or Email')+': ', "filters[name]", @filters[:name], {:id => 'filter-name-autocomplete',:size => 30}) %>
  6 + </p>
  7 +
  8 + <p><%= _('Roles:') %> </p>
  9 + <% @data[:roles].each do |r| %>
  10 + <%= labelled_check_box(r.name, 'filters[roles][]', r.id, @filters[:roles].include?(r.id.to_s), :add_hidden => false) %><br/>
  11 + <% end %>
  12 + <p>
  13 + <%= submit_button(:search, _('Search')) %>
  14 + </p>
  15 + <% end %>
  16 +<% end %>
  17 +
  18 +<%= javascript_include_tag params[:controller] %>
0 19 \ No newline at end of file
... ...
app/views/profile_members/_members_list.html.erb
1   -<% collection = @collection == :profile_admins ? profile.admins : profile.members_by_name %>
  1 +<% members = @data ? @data[:members] : profile.members_by('name') %>
  2 +<% collection = @collection == :profile_admins ? profile.admins : members %>
2 3 <% title = @title ? @title : _('Current members') %>
3 4 <% remove_action = @remove_action ? @remove_action : {:action => 'unassociate'} %>
4 5  
... ... @@ -9,6 +10,7 @@
9 10 <th><%= _('Member') %></th>
10 11 <th><%= _('Actions') %></th>
11 12 </tr>
  13 +
12 14 <% collection.each do |m| %>
13 15 <tr title="<%= m.name %>">
14 16 <td><%= link_to_profile m.short_name, m.identifier, :title => m.name %> </td>
... ... @@ -26,3 +28,8 @@
26 28 </tr>
27 29 <% end %>
28 30 </table>
  31 +<% if collection.empty? %>
  32 + <p>
  33 + <em><%= _('No members found to: %s') % profile.name %></em>
  34 + </p>
  35 +<% end %>
... ...
app/views/profile_members/index.html.erb
... ... @@ -2,6 +2,8 @@
2 2  
3 3 <%= render :partial => 'index_buttons' %>
4 4  
  5 +<%= render :partial => 'members_filter' %>
  6 +
5 7 <div id="members-list">
6 8 <%= render :partial => 'members_list' %>
7 9 </div>
... ...
db/migrate/20160224132937_add_data_to_mailing.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +class AddDataToMailing < ActiveRecord::Migration
  2 + def change
  3 + add_column :mailings, :data, :text
  4 + end
  5 +end
... ...
db/schema.rb
... ... @@ -11,7 +11,7 @@
11 11 #
12 12 # It's strongly recommended that you check this file into your version control system.
13 13  
14   -ActiveRecord::Schema.define(version: 20160202142247) do
  14 +ActiveRecord::Schema.define(version: 20160224132937) do
15 15  
16 16 # These are extensions that must be enabled in order to support this database
17 17 enable_extension "plpgsql"
... ... @@ -471,6 +471,7 @@ ActiveRecord::Schema.define(version: 20160202142247) do
471 471 t.string "locale"
472 472 t.datetime "created_at"
473 473 t.datetime "updated_at"
  474 + t.text "data"
474 475 end
475 476  
476 477 create_table "national_region_types", force: :cascade do |t|
... ...
public/designs/themes/noosfero/style.css
... ... @@ -48,3 +48,9 @@
48 48 width: 80px;
49 49 }
50 50  
  51 +.action-profile-send_mail .send-mail-recipients {
  52 + color: #888888;
  53 + padding: 10px;
  54 + width: 475px;
  55 + line-height: 15px;
  56 +}
... ...
public/javascripts/profile_members.js 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +(function($) {
  2 +
  3 + //Autocomplete to list members
  4 + $('#filter-name-autocomplete').autocomplete({
  5 + minLength:2,
  6 + source:function(request,response){
  7 + $.ajax({
  8 + url:document.location.pathname+'/search_members',
  9 + dataType:'json',
  10 + data:{
  11 + filter_name:request.term
  12 + },
  13 + success:response
  14 + });
  15 + }
  16 + });
  17 +
  18 +})(jQuery);
0 19 \ No newline at end of file
... ...
test/functional/profile_controller_test.rb
... ... @@ -1465,11 +1465,41 @@ class ProfileControllerTest &lt; ActionController::TestCase
1465 1465 create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
1466 1466 login_as('profile_moderator_user')
1467 1467 @controller.stubs(:locale).returns('pt')
  1468 +
1468 1469 assert_difference 'Delayed::Job.count', 1 do
1469 1470 post :send_mail, :profile => community.identifier, :mailing => {:subject => 'Hello', :body => 'We have some news'}
1470 1471 end
1471 1472 end
1472 1473  
  1474 + should 'send to members_filtered if available' do
  1475 + community = fast_create(Community)
  1476 + create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
  1477 + person = create_user('Any').person
  1478 + community.add_member(person)
  1479 + community.save!
  1480 + login_as('profile_moderator_user')
  1481 +
  1482 + post :send_mail, :profile => community.identifier, :mailing => {:subject => 'Hello', :body => 'We have some news'}
  1483 + assert_equivalent community.members, OrganizationMailing.last.recipients
  1484 +
  1485 + @request.session[:members_filtered] = [person.id]
  1486 + post :send_mail, :profile => community.identifier, :mailing => {:subject => 'RUN!!', :body => 'Run to the hills!!'}
  1487 + assert_equal [person], OrganizationMailing.last.recipients
  1488 + end
  1489 +
  1490 + should 'send email to all members if there is no valid member in members_filtered' do
  1491 + community = fast_create(Community)
  1492 + create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
  1493 + person = create_user('Any').person
  1494 + community.add_member(person)
  1495 + community.save!
  1496 + login_as('profile_moderator_user')
  1497 +
  1498 + @request.session[:members_filtered] = [Profile.last.id+1]
  1499 + post :send_mail, :profile => community.identifier, :mailing => {:subject => 'RUN!!', :body => 'Run to the hills!!'}
  1500 + assert_equivalent community.members, OrganizationMailing.last.recipients
  1501 + end
  1502 +
1473 1503 should 'save mailing' do
1474 1504 community = fast_create(Community)
1475 1505 create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
... ...
test/functional/profile_members_controller_test.rb
... ... @@ -31,6 +31,31 @@ class ProfileMembersControllerTest &lt; ActionController::TestCase
31 31 assert_template 'index'
32 32 end
33 33  
  34 + should 'access index and filter members by name and roles' do
  35 +
  36 + ent = fast_create(Enterprise, :identifier => 'test_enterprise', :name => 'test enterprise')
  37 + roles = {
  38 + :admin => Profile::Roles.admin(Environment.default),
  39 + :member => Profile::Roles.member(Environment.default)
  40 + }
  41 +
  42 + member = create_user('test_member', :email => 'testmember@test.com.br').person
  43 + member.add_role(roles[:member], ent)
  44 +
  45 + admin = create_user('test_admin').person
  46 + admin.add_role roles[:admin], ent
  47 +
  48 + user = create_user_with_permission('test_user', 'manage_memberships', ent)
  49 + login_as :test_user
  50 +
  51 + post :index, :profile => 'test_enterprise' , :filters => {:name => 'testmember@test.com.br', :roles => [roles[:member].id]}
  52 +
  53 + assert_response :success
  54 + assert_template 'index'
  55 +
  56 + assert_includes assigns(:data)[:members], member
  57 + end
  58 +
34 59 should 'show form to change role' do
35 60 ent = fast_create(Enterprise, :identifier => 'test_enterprise', :name => 'test enterprise')
36 61 role = Profile::Roles.member(Environment.default)
... ...
test/unit/organization_mailing_test.rb
... ... @@ -98,6 +98,11 @@ class OrganizationMailingTest &lt; ActiveSupport::TestCase
98 98 assert_equal [Person['user_one'], Person['user_two']], mailing.recipients
99 99 end
100 100  
  101 + should 'return recipients previously filtered' do
  102 + 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]})
  103 + assert_equivalent [Person['user_one'], Person['user_two']], mailing.recipients
  104 + end
  105 +
101 106 should 'return recipients according to limit' do
102 107 mailing = create(OrganizationMailing, :source => community, :subject => 'Hello', :body => 'We have some news', :person => person)
103 108 assert_equal [Person['user_one']], mailing.recipients(0, 1)
... ...
test/unit/profile_test.rb
... ... @@ -1816,6 +1816,21 @@ class ProfileTest &lt; ActiveSupport::TestCase
1816 1816 assert_equal [person], community.members
1817 1817 end
1818 1818  
  1819 + should 'return a list members by email of a community' do
  1820 + someone = create_user('Someone', email:'someone@test.com.br')
  1821 + someperson = create_user('Someperson',email:'someperson@test.com.br')
  1822 +
  1823 + community = fast_create(Community)
  1824 + community.add_member(someone.person)
  1825 + community.add_member(someperson.person)
  1826 +
  1827 + result = community.members_like 'email', '@test.com.br'
  1828 +
  1829 + assert_includes result, someone.person
  1830 + assert_includes result, someperson.person
  1831 +
  1832 + end
  1833 +
1819 1834 should 'count unique members of a community' do
1820 1835 person = fast_create(Person)
1821 1836 community = fast_create(Community)
... ...