diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index 297c0c07cf..f5426e9508 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -27,17 +27,24 @@ class MembersController < ApplicationController require_sudo_mode :create, :update, :destroy + include MembersHelper + def index scope = @project.memberships - @offset, @limit = api_offset_and_limit - @member_count = scope.count - @member_pages = Paginator.new @member_count, @limit, params['page'] - @offset ||= @member_pages.offset - @members = scope.includes(:principal, :roles).order(:id).limit(@limit).offset(@offset).to_a + @members = scope.includes(:principal, :roles).order(:id) respond_to do |format| format.html {head 406} - format.api + format.api do + @offset, @limit = api_offset_and_limit + @member_count = scope.count + @member_pages = Paginator.new @member_count, @limit, params['page'] + @offset ||= @member_pages.offset + @members = @members.limit(@limit).offset(@offset).to_a + end + format.csv do + send_data(members_to_csv(@members), type: 'text/csv; header=present', filename: "#{@project.identifier}-members.csv") + end end end diff --git a/app/helpers/members_helper.rb b/app/helpers/members_helper.rb index ed1f9ae4c2..1b7c3b1281 100644 --- a/app/helpers/members_helper.rb +++ b/app/helpers/members_helper.rb @@ -62,4 +62,23 @@ module MembersHelper content_tag('em', content.join(", "), :class => "info") end end + + def members_to_csv(members) + Redmine::Export::CSV.generate(encoding: params[:encoding]) do |csv| + # csv headers + csv << ['', l(:label_role), l(:field_principal), l(:label_project)] + + # csv lines + members.each do |member| + member.roles.each do |role| + csv << [ + member.principal.name, + role.name, + member.principal.is_a?(Group) ? l(:label_group) : l(:label_user), + member.project.name + ] + end + end + end + end end diff --git a/app/views/projects/settings/_members.html.erb b/app/views/projects/settings/_members.html.erb index 811b0051c5..db09d00d62 100644 --- a/app/views/projects/settings/_members.html.erb +++ b/app/views/projects/settings/_members.html.erb @@ -38,6 +38,19 @@ <% end %> +<% other_formats_links do |f| %> + <%= f.link_to_with_query_parameters "CSV", {}, :onclick => "showModal('csv-export-options', '330px'); return false;" %> +<% end %> + <% else %>

<%= l(:label_no_data) %>

<% end %> diff --git a/test/functional/members_controller_test.rb b/test/functional/members_controller_test.rb index a23cd74729..72d0e6dd28 100644 --- a/test/functional/members_controller_test.rb +++ b/test/functional/members_controller_test.rb @@ -27,6 +27,37 @@ class MembersControllerTest < Redmine::ControllerTest @request.session[:user_id] = 2 end + def test_index_with_csv_format_should_export_csv + project = Project.find(5) + get( + :index, + params: { + project_id: project.id, + format: 'csv' + } + ) + assert_response :success + assert_equal 'text/csv; header=present', response.media_type + + lines = response.body.chomp.split("\n") + # Number of lines + assert_equal project.memberships.sum{|m| m.roles.count } + 1, lines.size + # Header + assert_equal "\"\",Role,User or Group,Project", lines.first + # Details + to_test = [ + 'John Smith,Manager,User,Private child of eCookbook', + 'A Team,Manager,Group,Private child of eCookbook', + 'A Team,Developer,Group,Private child of eCookbook', + 'User Misc,Manager,User,Private child of eCookbook', + 'User Misc,Developer,User,Private child of eCookbook', + 'Redmine Admin,Manager,User,Private child of eCookbook' + ] + to_test.each do |expected| + assert_includes lines, expected + end + end + def test_new get(:new, :params => {:project_id => 1}) assert_response :success