diff --git a/apps/users/forms.py b/apps/users/forms.py index a5827d960..472a8892c 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -141,4 +141,4 @@ class UserGroupPrivateAssetPermissionForm(forms.ModelForm): class FileForm(forms.Form): - excel = forms.FileField() + users = forms.FileField() diff --git a/apps/users/templates/users/_user_import_modal.html b/apps/users/templates/users/_user_import_modal.html index 0993d26fe..4ee538908 100644 --- a/apps/users/templates/users/_user_import_modal.html +++ b/apps/users/templates/users/_user_import_modal.html @@ -1,16 +1,14 @@ {% extends '_modal.html' %} {% load i18n %} {% block modal_id %}user_import_modal{% endblock %} -{% block modal_title%}{% trans "Import User" %}{% endblock %} +{% block modal_title%}{% trans "Import user" %}{% endblock %} {% block modal_body %}

{% trans " * CSV format should be same as export" %}

-
+ {% csrf_token %}
- -
- -
+ +
{% endblock %} diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index 87fd018cc..dd2747102 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -3,17 +3,18 @@ {% block table_search %}
-{# #} -{# PDF#} - - CSV + + {% trans "Import" %} + + + {% trans "Export" %}
{% endblock %} {% block table_container %}
{% trans "Create user" %}
-
{% trans "Import user" %}
+{#
{% trans "Import user" %}
#} @@ -88,7 +89,7 @@ $(document).ready(function(){ }; var table = jumpserver.initDataTable(options); - $('.buttons-csv').click(function () { + $('.btn_export').click(function () { var users = []; var rows = table.rows('.selected').data(); $.each(rows, function (index, obj) { @@ -108,6 +109,26 @@ $(document).ready(function(){ }) }); + $('#btn_user_import').click(function() { + var $form = $('#fm_user_import'); + $form.find('.help-block').remove(); + function success (data) { + if (data.valid === false) { + var $help = $form.find('.help-block'); + $('', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_users')); + } else { +{# $('#user_import_modal').modal('hide');#} +{# var $data_table = $('#user_list_table').DataTable();#} +{# toastr.success("{% trans 'Import User Success.' %}");#} + $('', {class: 'help-block text-danger'}).html(data.errors.join(',')).insertAfter($('#id_users')); + $('', {class: 'help-block text-warning'}).html(data.updated.join(',')).insertAfter($('#id_users')); + $('', {class: 'help-block text-primary'}).html(data.created.join(',')).insertAfter($('#id_users')); +{# $data_table.ajax.reload();#} + } + } + $form.ajaxSubmit({success: success}); + }) + }).on('click', '#btn_bulk_update', function(){ var action = $('#slct_bulk_update').val(); var $data_table = $('#user_list_table').DataTable(); @@ -219,7 +240,7 @@ $(document).ready(function(){ new_groups = body.groups.map(Number); body.groups = new_groups; } - var $data_table = $('#user_list_table').DataTable() + var $data_table = $('#user_list_table').DataTable(); var post_list = []; $data_table.rows({selected: true}).every(function(){ var content = Object.assign({id: this.data().id}, body); @@ -237,22 +258,7 @@ $(document).ready(function(){ }; APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success}); $('#user_bulk_update_modal').modal('hide'); -}).on('click', '#btn_user_import', function() { - var $form = $('#fm_user_import'); - $form.find('.help-block').remove(); - function success (data) { - if (data.success === false) { - var $help = $form.find('.help-block'); - $('', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_excel')); - } else { - $('#user_import_modal').modal('hide'); - var $data_table = $('#user_list_table').DataTable(); - toastr.success("{% trans 'Import User Success.' %}"); - $data_table.ajax.reload(); - } - } - $form.ajaxSubmit({success: success}); -}) +}); {% endblock %} diff --git a/apps/users/urls/views_urls.py b/apps/users/urls/views_urls.py index a80b33d38..b51285800 100644 --- a/apps/users/urls/views_urls.py +++ b/apps/users/urls/views_urls.py @@ -24,7 +24,7 @@ urlpatterns = [ url(r'^user/(?P[0-9]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'), url(r'^user/(?P[0-9]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'), url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'), - url(r'^import/$', views.BulkImportUserView.as_view(), name='user-import'), + url(r'^user/import/$', views.BulkImportUserView.as_view(), name='user-import'), # url(r'^user/(?P[0-9]+)/assets-perm$', views.UserDetailView.as_view(), name='user-detail'), url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'), url(r'^user/(?P[0-9]+)/update$', views.UserUpdateView.as_view(), name='user-update'), diff --git a/apps/users/views.py b/apps/users/views.py index 6bc35f8f6..37c398012 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -5,11 +5,11 @@ import json import uuid from io import BytesIO -from reportlab.pdfgen import canvas import unicodecsv as csv from django import forms from django.utils import timezone from django.core.cache import cache +from django.db import IntegrityError from django.contrib.auth import login as auth_login, logout as auth_logout from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.messages.views import SuccessMessageMixin @@ -496,45 +496,41 @@ class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView): return self.render_json_response(data) def form_valid(self, form): - from openpyxl import load_workbook - try: - wb = load_workbook(form.cleaned_data['excel']) - ws = wb['users'] - except Exception as e: - print e - error = _('Not a valid Excel file.') - data = { - 'success': False, - 'msg': error - } + users_csv = form.cleaned_data['users'] + users_csv_f = csv.reader(users_csv, encoding='utf-8') + header_need = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"] + header = next(users_csv_f) + print(header) + if header != header_need: + data = {'valid': False, 'msg': 'Must be same format as export csv: name, ...'} return self.render_json_response(data) + created = [] + updated = [] errors = [] - for index, row in enumerate(ws.rows): - user_data = [cell.value for cell in row] - if len(user_data) != 4: - errors.append("Row {}: invalid user data format.".format(index)) - continue - username, email, enable_otp, role = user_data - data = { - 'username': username, - 'email': email, - 'enable_otp': True if enable_otp in ['T', '1', 1, True] else False, - 'role': role - } - form = forms.UserBulkImportForm(data, auto_id=False) - if form.is_valid(): - form.save() - else: - form_errors = form.errors.as_data() - for key, err_list in form_errors.iteritems(): - error_line = "{} :".format(key) - for errs in err_list: - error_line = "{}{}".format(error_line, ";".join([err for err in errs.messages])) - errors.append("Row {}: {}".format(index, error_line)) + for row in users_csv_f: + user_dict = dict(zip(header, row)) + groups_name = user_dict.pop('groups').split(',') + groups = UserGroup.objects.filter(name__in=groups_name) + try: + user = User.objects.create(**user_dict) + user.groups.add(*tuple(groups)) + user.save() + created.append(user_dict['username']) + except IntegrityError: + user = User.objects.filter(username=user_dict['username']) + user.update(**user_dict) + user[0].groups.add(*tuple(groups)) + updated.append(user_dict['username']) + except TypeError: + errors.append(user_dict['username']) + data = { - 'success': True if not errors else False, - 'msg': 'ok' if not errors else '
'.join(errors) + 'created': created, + 'updated': updated, + 'errors': errors, + 'valid': True, + 'msg': 'Created: {}. Updated: {}, Error: {}'.format(len(created), len(updated), len(errors)) } return self.render_json_response(data) @@ -543,22 +539,21 @@ class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView): class ExportUserCsvView(View): def get(self, request, *args, **kwargs): spm = request.GET.get('spm', '') - print(spm) users_id = cache.get(spm) if not users_id and not isinstance(users_id, list): return HttpResponse('May be expired', status=404) users = User.objects.filter(id__in=users_id) - filename = 'users-%s.csv' % timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H:%M:%D') + filename = 'users-%s.csv' % timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S') response = HttpResponse(content_type='application/csv') response['Content-Disposition'] = 'attachment; filename="%s"' % filename writer = csv.writer(response, delimiter=str(","), lineterminator='\n', quoting=csv.QUOTE_ALL, dialect='excel') - header = [_("name"), _('username'), _('email'), _('user group'), - _('role'), _('phone'), _('wechat'), _('comment')] + header = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"] writer.writerow(header) for user in users: - writer.writerow([user.name, user.username, user.email, ','.join([group.name for group in user.groups.all()]), + writer.writerow([user.name, user.username, user.email, + ','.join([group.name for group in user.groups.all()]), user.role, user.phone, user.wechat, user.comment]) return response