summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorGo MAEDA <maeda@farend.jp>2020-05-30 04:53:24 +0000
committerGo MAEDA <maeda@farend.jp>2020-05-30 04:53:24 +0000
commitebf5d219c9a8211c083dede3b9b9048bc68f257e (patch)
treedc036b5d5ee7f5b5ca4228c2cdcc0726c491a48c /app
parent454bed8143baa7e7b51f7571196b068ff4297b93 (diff)
downloadredmine-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.rb10
-rw-r--r--app/models/user_import.rb118
-rw-r--r--app/views/imports/_users_fields_mapping.html.erb53
-rw-r--r--app/views/imports/_users_mapping.html.erb6
-rw-r--r--app/views/imports/_users_mapping.js.erb1
-rw-r--r--app/views/imports/_users_saved_objects.html.erb24
-rw-r--r--app/views/imports/mapping.html.erb2
-rw-r--r--app/views/imports/new.html.erb2
-rw-r--r--app/views/imports/run.html.erb2
-rw-r--r--app/views/imports/settings.html.erb2
-rw-r--r--app/views/imports/show.html.erb2
-rw-r--r--app/views/users/index.html.erb5
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 + " &#187;".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 + " &#187;".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>