Project

General

Profile

Feature #37480 » feature-37480-v2.patch

Mizuki ISHIKAWA, 2025-12-17 03:55

View differences:

app/controllers/members_controller.rb
28 28

  
29 29
  require_sudo_mode :create, :update, :destroy
30 30

  
31
  include MembersHelper
32

  
31 33
  def index
32 34
    scope = @project.memberships
33
    @offset, @limit = api_offset_and_limit
34
    @member_count = scope.count
35
    @member_pages = Paginator.new @member_count, @limit, params['page']
36
    @offset ||= @member_pages.offset
37
    @members = scope.includes(:principal, :roles).order(:id).limit(@limit).offset(@offset).to_a
35
    @members = scope.includes(:principal, :roles).order(:id)
38 36

  
39 37
    respond_to do |format|
40
      format.html {head :not_acceptable}
41
      format.api
38
      format.html {head 406}
39
      format.api do
40
        @offset, @limit = api_offset_and_limit
41
        @member_count = scope.count
42
        @member_pages = Paginator.new @member_count, @limit, params['page']
43
        @offset ||= @member_pages.offset
44
        @members = @members.limit(@limit).offset(@offset).to_a
45
      end
46
      format.csv do
47
        send_data(members_to_csv(@members), type: 'text/csv; header=present', filename: "#{@project.identifier}-members.csv")
48
      end
42 49
    end
43 50
  end
44 51

  
app/helpers/members_helper.rb
62 62
      content_tag('em', content.join(", "), :class => "info")
63 63
    end
64 64
  end
65

  
66
  def members_to_csv(members)
67
    Redmine::Export::CSV.generate(encoding: params[:encoding]) do |csv|
68
      # csv headers
69
      csv << ['', l(:label_role), l(:field_principal), l(:label_project)]
70

  
71
      # csv lines
72
      members.each do |member|
73
        member.roles.each do |role|
74
          csv << [
75
            member.principal.name,
76
            role.name,
77
            member.principal.is_a?(Group) ? l(:label_group) : l(:label_user),
78
            member.project.name
79
          ]
80
        end
81
      end
82
    end
83
  end
65 84
end
app/views/projects/settings/_members.html.erb
45 45
<% end %>
46 46
  </tbody>
47 47
</table>
48
<% other_formats_links do |f| %>
49
  <%= f.link_to_with_query_parameters "CSV", {}, :onclick => "showModal('csv-export-options', '330px'); return false;" %>
50
<% end %>
51
<div id="csv-export-options" style="display: none;">
52
  <h3 class="title"><%= l(:label_export_options, :export_format => 'CSV') %></h3>
53
  <%= form_tag(project_memberships_path(project_id: @project.id, format: 'csv'), :method => :get, :id => 'csv-export-form') do %>
54
  <%= export_csv_encoding_select_tag %>
55
  <p class="buttons">
56
    <%= submit_tag l(:button_export), :name => nil, :onclick => 'hideModal(this);', :data => {:disable_with => false} %>
57
    <%= link_to_function l(:button_cancel), 'hideModal(this);' %>
58
  </p>
59
  <% end %>
60
</div>
48 61
<% else %>
49 62
<p class="nodata"><%= l(:label_no_data) %></p>
50 63
<% end %>
test/functional/members_controller_test.rb
25 25
    @request.session[:user_id] = 2
26 26
  end
27 27

  
28
  def test_index_with_csv_format_should_export_csv
29
    project = Project.find(5)
30
    get(
31
      :index,
32
      params: {
33
        project_id: project.id,
34
        format: 'csv'
35
      }
36
    )
37
    assert_response :success
38
    assert_equal 'text/csv; header=present', response.media_type
39

  
40
    lines = response.body.chomp.split("\n")
41
    # Number of lines
42
    assert_equal project.memberships.sum{|m| m.roles.count } + 1, lines.size
43
    # Header
44
    assert_equal "\"\",Role,User or Group,Project", lines.first
45
    # Details
46
    to_test = [
47
      'John Smith,Manager,User,Private child of eCookbook',
48
      'A Team,Manager,Group,Private child of eCookbook',
49
      'A Team,Developer,Group,Private child of eCookbook',
50
      'User Misc,Manager,User,Private child of eCookbook',
51
      'User Misc,Developer,User,Private child of eCookbook',
52
      'Redmine Admin,Manager,User,Private child of eCookbook'
53
    ]
54
    to_test.each do |expected|
55
      assert_includes lines, expected
56
    end
57
  end
58

  
28 59
  def test_new
29 60
    get(:new, :params => {:project_id => 1})
30 61
    assert_response :success
(3-3/3)