diff options
author | Go MAEDA <maeda@farend.jp> | 2020-05-30 04:53:24 +0000 |
---|---|---|
committer | Go MAEDA <maeda@farend.jp> | 2020-05-30 04:53:24 +0000 |
commit | ebf5d219c9a8211c083dede3b9b9048bc68f257e (patch) | |
tree | dc036b5d5ee7f5b5ca4228c2cdcc0726c491a48c /app | |
parent | 454bed8143baa7e7b51f7571196b068ff4297b93 (diff) | |
download | redmine-ebf5d219c9a8211c083dede3b9b9048bc68f257e.tar.gz redmine-ebf5d219c9a8211c083dede3b9b9048bc68f257e.zip |
Import user accounts from CSV file (#33102).
Patch by Takenori TAKAKI.
git-svn-id: http://svn.redmine.org/redmine/trunk@19799 e93f8b46-1217-0410-a6f0-8f06a7374b81
Diffstat (limited to 'app')
-rw-r--r-- | app/helpers/application_helper.rb | 10 | ||||
-rw-r--r-- | app/models/user_import.rb | 118 | ||||
-rw-r--r-- | app/views/imports/_users_fields_mapping.html.erb | 53 | ||||
-rw-r--r-- | app/views/imports/_users_mapping.html.erb | 6 | ||||
-rw-r--r-- | app/views/imports/_users_mapping.js.erb | 1 | ||||
-rw-r--r-- | app/views/imports/_users_saved_objects.html.erb | 24 | ||||
-rw-r--r-- | app/views/imports/mapping.html.erb | 2 | ||||
-rw-r--r-- | app/views/imports/new.html.erb | 2 | ||||
-rw-r--r-- | app/views/imports/run.html.erb | 2 | ||||
-rw-r--r-- | app/views/imports/settings.html.erb | 2 | ||||
-rw-r--r-- | app/views/imports/show.html.erb | 2 | ||||
-rw-r--r-- | app/views/users/index.html.erb | 5 |
12 files changed, 222 insertions, 5 deletions
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index bf55e05a5..1b4a65b4c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1700,6 +1700,16 @@ module ApplicationHelper } end + def render_if_exist(options = {}, locals = {}, &block) + if options[:partial] + if lookup_context.exists?(options[:partial], lookup_context.prefixes, true) + render(options, locals, &block) + end + else + render(options, locals, &block) + end + end + private def wiki_helper diff --git a/app/models/user_import.rb b/app/models/user_import.rb new file mode 100644 index 000000000..21c3e530e --- /dev/null +++ b/app/models/user_import.rb @@ -0,0 +1,118 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2020 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +class UserImport < Import + AUTO_MAPPABLE_FIELDS = { + 'login' => 'field_login', + 'firstname' => 'field_firstname', + 'lastname' => 'field_lastname', + 'mail' => 'field_mail', + 'language' => 'field_language', + 'admin' => 'field_admin', + 'auth_source' => 'field_auth_source', + 'password' => 'field_password', + 'must_change_passwd' => 'field_must_change_passwd', + 'status' => 'field_status' + } + + def self.menu_item + :users + end + + def self.layout + 'admin' + end + + def self.authorized?(user) + user.admin? + end + + # Returns the objects that were imported + def saved_objects + User.where(:id => saved_items.pluck(:obj_id)).order(:id) + end + + def mappable_custom_fields + UserCustomField.all + end + + private + + def build_object(row, item) + object = User.new + + attributes = { + :login => row_value(row, 'login'), + :firstname => row_value(row, 'firstname'), + :lastname => row_value(row, 'lastname'), + :mail => row_value(row, 'mail') + } + + lang = nil + if language = row_value(row, 'language') + lang = find_language(language) + end + attributes[:language] = lang || Setting.default_language + + if admin = row_value(row, 'admin') + if yes?(admin) + attributes['admin'] = '1' + end + end + + if auth_source_name = row_value(row, 'auth_source') + if auth_source = AuthSource.find_by(:name => auth_source_name) + attributes[:auth_source_id] = auth_source.id + end + end + + if password = row_value(row, 'password') + object.password = password + object.password_confirmation = password + end + + if must_change_passwd = row_value(row, 'must_change_passwd') + if yes?(must_change_passwd) + attributes[:must_change_passwd] = '1' + end + end + + if status_name = row_value(row, 'status') + if status = User::LABEL_BY_STATUS.key(status_name) + attributes[:status] = status + end + end + + attributes['custom_field_values'] = object.custom_field_values.each_with_object({}) do |v, h| + value = + case v.custom_field.field_format + when 'date' + row_date(row, "cf_#{v.custom_field.id}") + else + row_value(row, "cf_#{v.custom_field.id}") + end + if value + h[v.custom_field.id.to_s] = v.custom_field.value_from_keyword(value, object) + end + end + + object.send(:safe_attributes=, attributes, user) + object + end +end diff --git a/app/views/imports/_users_fields_mapping.html.erb b/app/views/imports/_users_fields_mapping.html.erb new file mode 100644 index 000000000..f2b2aaf90 --- /dev/null +++ b/app/views/imports/_users_fields_mapping.html.erb @@ -0,0 +1,53 @@ +<div class="splitcontent"> + <div class="splitcontentleft"> + <p> + <label for="import_mapping_login"><%= l(:field_login) %></label> + <%= mapping_select_tag @import, 'login', :required => true %> + </p> + <p> + <label for="import_mapping_firstname"><%= l(:field_firstname) %></label> + <%= mapping_select_tag @import, 'firstname', :required => true %> + </p> + <p> + <label for="import_mapping_lastname"><%= l(:field_lastname) %></label> + <%= mapping_select_tag @import, 'lastname', :required => true %> + </p> + <p> + <label for="import_mapping_mail"><%= l(:field_mail) %></label> + <%= mapping_select_tag @import, 'mail' %> + </p> + <p> + <label for="import_mapping_language"><%= l(:field_language) %></label> + <%= mapping_select_tag @import, 'language' %> + </p> + <p> + <label for="import_mapping_admin"><%= l(:field_admin) %></label> + <%= mapping_select_tag @import, 'admin' %> + </p> + <p> + <label for="import_mapping_auth_source_id"><%= l(:field_auth_source) %></label> + <%= mapping_select_tag @import, 'auth_source' %> + </p> + <p> + <label for="import_mapping_password"><%= l(:field_password) %></label> + <%= mapping_select_tag @import, 'password' %> + </p> + <p> + <label for="import_mapping_must_change_passwd"><%= l(:field_must_change_passwd) %></label> + <%= mapping_select_tag @import, 'must_change_passwd' %> + </p> + <p> + <label for="import_mapping_status"><%= l(:field_status) %></label> + <%= mapping_select_tag @import, 'status' %> + </p> + </div> + + <div class="splitcontentright"> + <% @custom_fields.each do |field| %> + <p> + <label for="import_mapping_cf_<%= field.id %>"><%= field.name %></label> + <%= mapping_select_tag @import, "cf_#{field.id}", :required => field.is_required? %> + </p> + <% end %> + </div> +</div> diff --git a/app/views/imports/_users_mapping.html.erb b/app/views/imports/_users_mapping.html.erb new file mode 100644 index 000000000..8b9c758f9 --- /dev/null +++ b/app/views/imports/_users_mapping.html.erb @@ -0,0 +1,6 @@ +<fieldset class="box tabular"> + <legend><%= l(:label_fields_mapping) %></legend> + <div id="fields-mapping"> + <%= render :partial => 'users_fields_mapping' %> + </div> +</fieldset> diff --git a/app/views/imports/_users_mapping.js.erb b/app/views/imports/_users_mapping.js.erb new file mode 100644 index 000000000..a24d3bb86 --- /dev/null +++ b/app/views/imports/_users_mapping.js.erb @@ -0,0 +1 @@ +$('#fields-mapping').html('<%= escape_javascript(render :partial => 'users_fields_mapping') %>'); diff --git a/app/views/imports/_users_saved_objects.html.erb b/app/views/imports/_users_saved_objects.html.erb new file mode 100644 index 000000000..5e11a9289 --- /dev/null +++ b/app/views/imports/_users_saved_objects.html.erb @@ -0,0 +1,24 @@ +<table id="saved-items" class="list"> + <thead> + <tr> + <th><%= t(:field_login) %></th> + <th><%= t(:field_firstname) %></th> + <th><%= t(:field_lastname) %></th> + <th><%= t(:field_mail) %></th> + <th><%= t(:field_admin) %></th> + <th><%= t(:field_status) %></th> + </tr> + </thead> + <tbody> + <% saved_objects.each do |user| %> + <tr> + <td><%= avatar(user, :size => "14") %><%= link_to user.login, edit_user_path(user) %></td> + <td><%= user.firstname %></td> + <td><%= user.lastname %></td> + <td><%= mail_to(user.mail) %></td> + <td><%= checked_image user.admin? %></td> + <td><%= l(("status_#{User::LABEL_BY_STATUS[user.status]}")) %> + </tr> + <% end %> + </tbody> +</table> diff --git a/app/views/imports/mapping.html.erb b/app/views/imports/mapping.html.erb index d5095bf50..448ed8e0d 100644 --- a/app/views/imports/mapping.html.erb +++ b/app/views/imports/mapping.html.erb @@ -23,7 +23,7 @@ </p> <% end %> -<%= render :partial => "#{import_partial_prefix}_sidebar" %> +<%= render_if_exist :partial => "#{import_partial_prefix}_sidebar" %> <%= javascript_tag do %> $(document).ready(function() { diff --git a/app/views/imports/new.html.erb b/app/views/imports/new.html.erb index e7ca82428..e91ea80a7 100644 --- a/app/views/imports/new.html.erb +++ b/app/views/imports/new.html.erb @@ -12,4 +12,4 @@ <p><%= submit_tag l(:label_next).html_safe + " »".html_safe, :name => nil %></p> <% end %> -<%= render :partial => "#{import_partial_prefix}_sidebar" %> +<%= render_if_exist :partial => "#{import_partial_prefix}_sidebar" %> diff --git a/app/views/imports/run.html.erb b/app/views/imports/run.html.erb index 50b47836d..4a1650f43 100644 --- a/app/views/imports/run.html.erb +++ b/app/views/imports/run.html.erb @@ -4,7 +4,7 @@ <div id="import-progress"><div id="progress-label">0 / <%= @import.total_items.to_i %></div></div> </div> -<%= render :partial => "#{import_partial_prefix}_sidebar" %> +<%= render_if_exist :partial => "#{import_partial_prefix}_sidebar" %> <%= javascript_tag do %> $(document).ready(function() { diff --git a/app/views/imports/settings.html.erb b/app/views/imports/settings.html.erb index c538ea983..09a7d5d6f 100644 --- a/app/views/imports/settings.html.erb +++ b/app/views/imports/settings.html.erb @@ -31,4 +31,4 @@ <p><%= submit_tag l(:label_next).html_safe + " »".html_safe, :name => nil %></p> <% end %> -<%= render :partial => "#{import_partial_prefix}_sidebar" %> +<%= render_if_exist :partial => "#{import_partial_prefix}_sidebar" %> diff --git a/app/views/imports/show.html.erb b/app/views/imports/show.html.erb index ca963ab37..6530812ac 100644 --- a/app/views/imports/show.html.erb +++ b/app/views/imports/show.html.erb @@ -27,4 +27,4 @@ </table> <% end %> -<%= render :partial => "#{import_partial_prefix}_sidebar" %> +<%= render_if_exist :partial => "#{import_partial_prefix}_sidebar" %> diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 75c9eb465..b92df96f1 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -1,5 +1,10 @@ <div class="contextual"> <%= link_to l(:label_user_new), new_user_path, :class => 'icon icon-add' %> + <%= actions_dropdown do %> + <% if User.current.allowed_to?(:import_users, nil, :global => true) %> + <%= link_to l(:button_import), new_users_import_path, :class => 'icon icon-import' %> + <% end %> + <% end %> </div> <h2><%=l(:label_user_plural)%></h2> |