[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