Commit 80ebad1ff0bf5f7384a2048ba1b4ba60f847fad3
Committed by
Marcos Pereira
1 parent
ea20a3dc
Exists in
web_steps_improvements
and in
9 other branches
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>
Showing
19 changed files
with
202 additions
and
15 deletions
Show diff stats
app/controllers/my_profile/profile_members_controller.rb
... | ... | @@ -2,8 +2,16 @@ class ProfileMembersController < 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 < 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 < 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 'mailing_job' |
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 < 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 < 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 < 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 < 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)) %> | ... | ... |
... | ... | @@ -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
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
... | ... | @@ -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 < 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 < 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 < 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 < 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) | ... | ... |