diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 04d9a78ada..aed4cd83fa 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -28,6 +28,7 @@ class UsersController < ApplicationController include SortHelper helper :custom_fields include CustomFieldsHelper + include UsersHelper helper :principal_memberships helper :activities include ActivitiesHelper @@ -61,6 +62,9 @@ class UsersController < ApplicationController @groups = Group.givable.sort render :layout => !request.xhr? } + format.csv { + send_data(users_to_csv(scope.order(sort_clause)), :type => 'text/csv; header=present', :filename => 'users.csv') + } format.api end end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index d7812867be..c0d9544999 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -18,12 +18,11 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. module UsersHelper + include ApplicationHelper + def users_status_options_for_select(selected) user_count_by_status = User.group('status').count.to_hash - options_for_select([[l(:label_all), ''], - ["#{l(:status_active)} (#{user_count_by_status[1].to_i})", '1'], - ["#{l(:status_registered)} (#{user_count_by_status[2].to_i})", '2'], - ["#{l(:status_locked)} (#{user_count_by_status[3].to_i})", '3']], selected.to_s) + options_for_select([[l(:label_all), '']] + (User.valid_statuses.map {|c| ["#{l('status_' + User::LABEL_BY_STATUS[c])} (#{user_count_by_status[c].to_i})", c]}), selected.to_s) end def user_mail_notification_options(user) @@ -61,4 +60,32 @@ module UsersHelper end tabs end + + def users_to_csv(users) + Redmine::Export::CSV.generate(:encoding => params[:encoding]) do |csv| + columns = [ + 'login', + 'firstname', + 'lastname', + 'mail', + 'admin', + 'created_on', + 'last_login_on', + 'status' + ] + + # csv header fields + csv << columns.map{|column| l('field_' + column)} + # csv lines + users.each do |user| + csv << columns.map do |column| + if column == 'status' + l(("status_#{User::LABEL_BY_STATUS[user.status]}")) + else + format_object(user.send(column), false) + end + end + end + end + end end diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 3fc82b6401..75c9eb465c 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -4,7 +4,7 @@

<%=l(:label_user_plural)%>

-<%= form_tag(users_path, :method => :get) do %> +<%= form_tag(users_path, { :method => :get, :id => 'users_form' }) do %>
<%= l(:label_filter_plural) %> <%= select_tag 'status', users_status_options_for_select(@status), :class => "small", :onchange => "this.form.submit(); return false;" %> @@ -19,6 +19,7 @@ <%= submit_tag l(:button_apply), :class => "small", :name => nil %> <%= link_to l(:button_clear), users_path, :class => 'icon icon-reload' %>
+<%= hidden_field_tag 'encoding', l(:general_csv_encoding) unless l(:general_csv_encoding).casecmp('UTF-8') == 0 %> <% end %>   @@ -55,6 +56,27 @@ <%= pagination_links_full @user_pages, @user_count %> +<% other_formats_links do |f| %> + <%= f.link_to_with_query_parameters 'CSV', {}, :onclick => "showModal('csv-export-options', '330px'); return false;" %> +<% end %> + +<%= javascript_tag do %> +$(document).ready(function(){ + $('input#csv-export-button').click(function(){ + $('form input#encoding').val($('select#encoding option:selected').val()); + $('form#users_form').attr('action', "<%= users_path(:format => 'csv') %>").submit(); + $('form#users_form').attr('action', '<%= users_path %>'); + hideModal(this); + }); +}); +<% end %> <% else %>

<%= l(:label_no_data) %>

<% end %> diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb index 42dae0f507..1f2dd4f326 100644 --- a/test/functional/users_controller_test.rb +++ b/test/functional/users_controller_test.rb @@ -64,6 +64,47 @@ class UsersControllerTest < Redmine::ControllerTest end end + def test_index_csv + with_settings :default_language => 'en' do + get :index, :params => { :format => 'csv' } + assert_response :success + + assert_equal User.logged.status(1).count, response.body.chomp.split("\n").size - 1 + assert_include 'active', response.body + assert_not_include 'locked', response.body + assert_equal 'text/csv; header=present', @response.content_type + end + end + + def test_index_csv_with_status_filter + with_settings :default_language => 'en' do + get :index, :params => { :status => 3, :format => 'csv' } + assert_response :success + + assert_equal User.logged.status(3).count, response.body.chomp.split("\n").size - 1 + assert_include 'locked', response.body + assert_not_include 'active', response.body + assert_equal 'text/csv; header=present', @response.content_type + end + end + + def test_index_csv_with_name_filter + get :index, :params => {:name => 'John', :format => 'csv'} + assert_response :success + + assert_equal User.logged.like('John').count, response.body.chomp.split("\n").size - 1 + assert_include 'John', response.body + assert_equal 'text/csv; header=present', @response.content_type + end + + def test_index_csv_with_group_filter + get :index, :params => {:group_id => '10', :format => 'csv'} + assert_response :success + + assert_equal Group.find(10).users.count, response.body.chomp.split("\n").size - 1 + assert_equal 'text/csv; header=present', @response.content_type + end + def test_show @request.session[:user_id] = nil get :show, :params => {:id => 2}