mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-07-05 11:06:34 +00:00
Add export and import
This commit is contained in:
parent
be99eb82e8
commit
e28f7a3bec
@ -141,4 +141,4 @@ class UserGroupPrivateAssetPermissionForm(forms.ModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class FileForm(forms.Form):
|
class FileForm(forms.Form):
|
||||||
excel = forms.FileField()
|
users = forms.FileField()
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
{% extends '_modal.html' %}
|
{% extends '_modal.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block modal_id %}user_import_modal{% endblock %}
|
{% block modal_id %}user_import_modal{% endblock %}
|
||||||
{% block modal_title%}{% trans "Import User" %}{% endblock %}
|
{% block modal_title%}{% trans "Import user" %}{% endblock %}
|
||||||
{% block modal_body %}
|
{% block modal_body %}
|
||||||
<p class="text-success">{% trans " * CSV format should be same as export" %}</p>
|
<p class="text-success">{% trans " * CSV format should be same as export" %}</p>
|
||||||
<form method="post" class="form-horizontal" action="{% url 'users:user-import' %}" id="fm_user_import" enctype="multipart/form-data">
|
<form method="post" action="{% url 'users:user-import' %}" id="fm_user_import" enctype="multipart/form-data">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2 col-lg-2 " for="id_excel">{% trans "CSV" %}</label>
|
<label class="control-label" for="id_users">{% trans "Users csv file" %}</label>
|
||||||
<div class=" col-sm-9 col-lg-9 ">
|
<input id="id_users" type="file" name="users" />
|
||||||
<input id="id_excel" type="file" name="excel" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -3,17 +3,18 @@
|
|||||||
{% block table_search %}
|
{% block table_search %}
|
||||||
<div class="html5buttons">
|
<div class="html5buttons">
|
||||||
<div class="dt-buttons btn-group">
|
<div class="dt-buttons btn-group">
|
||||||
{# <a class="btn btn-default buttons-pdf" tabindex="0" href="#">#}
|
<a class="btn btn-default btn_import" data-toggle="modal" data-target="#user_import_modal" tabindex="0">
|
||||||
{# <span>PDF</span></a>#}
|
<span>{% trans "Import" %}</span>
|
||||||
<a class="btn btn-default buttons-csv" tabindex="0" href="#">
|
</a>
|
||||||
<span>CSV</span>
|
<a class="btn btn-default btn_export" tabindex="0">
|
||||||
|
<span>{% trans "Export" %}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block table_container %}
|
{% block table_container %}
|
||||||
<div class="uc pull-left m-l-5 m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create user" %} </a></div>
|
<div class="uc pull-left m-l-5 m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create user" %} </a></div>
|
||||||
<div class="uc pull-left"><a href="javascript:void(0);" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#user_import_modal"> {% trans "Import user" %} </a></div>
|
{#<div class="uc pull-left"><a href="javascript:void(0);" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#user_import_modal"> {% trans "Import user" %} </a></div>#}
|
||||||
<table class="table table-striped table-bordered table-hover " id="user_list_table" >
|
<table class="table table-striped table-bordered table-hover " id="user_list_table" >
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -88,7 +89,7 @@ $(document).ready(function(){
|
|||||||
};
|
};
|
||||||
var table = jumpserver.initDataTable(options);
|
var table = jumpserver.initDataTable(options);
|
||||||
|
|
||||||
$('.buttons-csv').click(function () {
|
$('.btn_export').click(function () {
|
||||||
var users = [];
|
var users = [];
|
||||||
var rows = table.rows('.selected').data();
|
var rows = table.rows('.selected').data();
|
||||||
$.each(rows, function (index, obj) {
|
$.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');
|
||||||
|
$('<span />', {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.' %}");#}
|
||||||
|
$('<span />', {class: 'help-block text-danger'}).html(data.errors.join(',')).insertAfter($('#id_users'));
|
||||||
|
$('<span />', {class: 'help-block text-warning'}).html(data.updated.join(',')).insertAfter($('#id_users'));
|
||||||
|
$('<span />', {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(){
|
}).on('click', '#btn_bulk_update', function(){
|
||||||
var action = $('#slct_bulk_update').val();
|
var action = $('#slct_bulk_update').val();
|
||||||
var $data_table = $('#user_list_table').DataTable();
|
var $data_table = $('#user_list_table').DataTable();
|
||||||
@ -219,7 +240,7 @@ $(document).ready(function(){
|
|||||||
new_groups = body.groups.map(Number);
|
new_groups = body.groups.map(Number);
|
||||||
body.groups = new_groups;
|
body.groups = new_groups;
|
||||||
}
|
}
|
||||||
var $data_table = $('#user_list_table').DataTable()
|
var $data_table = $('#user_list_table').DataTable();
|
||||||
var post_list = [];
|
var post_list = [];
|
||||||
$data_table.rows({selected: true}).every(function(){
|
$data_table.rows({selected: true}).every(function(){
|
||||||
var content = Object.assign({id: this.data().id}, body);
|
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});
|
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});
|
||||||
$('#user_bulk_update_modal').modal('hide');
|
$('#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');
|
|
||||||
$('<span />', {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});
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ urlpatterns = [
|
|||||||
url(r'^user/(?P<pk>[0-9]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
|
url(r'^user/(?P<pk>[0-9]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
|
||||||
url(r'^user/(?P<pk>[0-9]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'),
|
url(r'^user/(?P<pk>[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'^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<pk>[0-9]+)/assets-perm$', views.UserDetailView.as_view(), name='user-detail'),
|
# url(r'^user/(?P<pk>[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/create$', views.UserCreateView.as_view(), name='user-create'),
|
||||||
url(r'^user/(?P<pk>[0-9]+)/update$', views.UserUpdateView.as_view(), name='user-update'),
|
url(r'^user/(?P<pk>[0-9]+)/update$', views.UserUpdateView.as_view(), name='user-update'),
|
||||||
|
@ -5,11 +5,11 @@ import json
|
|||||||
import uuid
|
import uuid
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from reportlab.pdfgen import canvas
|
|
||||||
import unicodecsv as csv
|
import unicodecsv as csv
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.core.cache import cache
|
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 import login as auth_login, logout as auth_logout
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
@ -496,45 +496,41 @@ class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
|
|||||||
return self.render_json_response(data)
|
return self.render_json_response(data)
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
from openpyxl import load_workbook
|
users_csv = form.cleaned_data['users']
|
||||||
try:
|
users_csv_f = csv.reader(users_csv, encoding='utf-8')
|
||||||
wb = load_workbook(form.cleaned_data['excel'])
|
header_need = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
|
||||||
ws = wb['users']
|
header = next(users_csv_f)
|
||||||
except Exception as e:
|
print(header)
|
||||||
print e
|
if header != header_need:
|
||||||
error = _('Not a valid Excel file.')
|
data = {'valid': False, 'msg': 'Must be same format as export csv: name, ...'}
|
||||||
data = {
|
|
||||||
'success': False,
|
|
||||||
'msg': error
|
|
||||||
}
|
|
||||||
return self.render_json_response(data)
|
return self.render_json_response(data)
|
||||||
|
|
||||||
|
created = []
|
||||||
|
updated = []
|
||||||
errors = []
|
errors = []
|
||||||
for index, row in enumerate(ws.rows):
|
for row in users_csv_f:
|
||||||
user_data = [cell.value for cell in row]
|
user_dict = dict(zip(header, row))
|
||||||
if len(user_data) != 4:
|
groups_name = user_dict.pop('groups').split(',')
|
||||||
errors.append("Row {}: invalid user data format.".format(index))
|
groups = UserGroup.objects.filter(name__in=groups_name)
|
||||||
continue
|
try:
|
||||||
username, email, enable_otp, role = user_data
|
user = User.objects.create(**user_dict)
|
||||||
data = {
|
user.groups.add(*tuple(groups))
|
||||||
'username': username,
|
user.save()
|
||||||
'email': email,
|
created.append(user_dict['username'])
|
||||||
'enable_otp': True if enable_otp in ['T', '1', 1, True] else False,
|
except IntegrityError:
|
||||||
'role': role
|
user = User.objects.filter(username=user_dict['username'])
|
||||||
}
|
user.update(**user_dict)
|
||||||
form = forms.UserBulkImportForm(data, auto_id=False)
|
user[0].groups.add(*tuple(groups))
|
||||||
if form.is_valid():
|
updated.append(user_dict['username'])
|
||||||
form.save()
|
except TypeError:
|
||||||
else:
|
errors.append(user_dict['username'])
|
||||||
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))
|
|
||||||
data = {
|
data = {
|
||||||
'success': True if not errors else False,
|
'created': created,
|
||||||
'msg': 'ok' if not errors else '<br />'.join(errors)
|
'updated': updated,
|
||||||
|
'errors': errors,
|
||||||
|
'valid': True,
|
||||||
|
'msg': 'Created: {}. Updated: {}, Error: {}'.format(len(created), len(updated), len(errors))
|
||||||
}
|
}
|
||||||
return self.render_json_response(data)
|
return self.render_json_response(data)
|
||||||
|
|
||||||
@ -543,22 +539,21 @@ class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
|
|||||||
class ExportUserCsvView(View):
|
class ExportUserCsvView(View):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
spm = request.GET.get('spm', '')
|
spm = request.GET.get('spm', '')
|
||||||
print(spm)
|
|
||||||
users_id = cache.get(spm)
|
users_id = cache.get(spm)
|
||||||
if not users_id and not isinstance(users_id, list):
|
if not users_id and not isinstance(users_id, list):
|
||||||
return HttpResponse('May be expired', status=404)
|
return HttpResponse('May be expired', status=404)
|
||||||
|
|
||||||
users = User.objects.filter(id__in=users_id)
|
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 = HttpResponse(content_type='application/csv')
|
||||||
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
|
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
|
||||||
writer = csv.writer(response, delimiter=str(","), lineterminator='\n',
|
writer = csv.writer(response, delimiter=str(","), lineterminator='\n',
|
||||||
quoting=csv.QUOTE_ALL, dialect='excel')
|
quoting=csv.QUOTE_ALL, dialect='excel')
|
||||||
header = [_("name"), _('username'), _('email'), _('user group'),
|
header = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
|
||||||
_('role'), _('phone'), _('wechat'), _('comment')]
|
|
||||||
writer.writerow(header)
|
writer.writerow(header)
|
||||||
for user in users:
|
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])
|
user.role, user.phone, user.wechat, user.comment])
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user