Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
J
jumpserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
jumpserver
Commits
e28f7a3b
Commit
e28f7a3b
authored
Nov 23, 2016
by
ibuler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add export and import
parent
be99eb82
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
71 additions
and
72 deletions
+71
-72
forms.py
apps/users/forms.py
+1
-1
_user_import_modal.html
apps/users/templates/users/_user_import_modal.html
+4
-6
user_list.html
apps/users/templates/users/user_list.html
+29
-23
views_urls.py
apps/users/urls/views_urls.py
+1
-1
views.py
apps/users/views.py
+36
-41
No files found.
apps/users/forms.py
View file @
e28f7a3b
...
@@ -141,4 +141,4 @@ class UserGroupPrivateAssetPermissionForm(forms.ModelForm):
...
@@ -141,4 +141,4 @@ class UserGroupPrivateAssetPermissionForm(forms.ModelForm):
class
FileForm
(
forms
.
Form
):
class
FileForm
(
forms
.
Form
):
excel
=
forms
.
FileField
()
users
=
forms
.
FileField
()
apps/users/templates/users/_user_import_modal.html
View file @
e28f7a3b
{% 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
U
ser" %}{% endblock %}
{% block modal_title%}{% trans "Import
u
ser" %}{% 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 %}
...
...
apps/users/templates/users/user_list.html
View file @
e28f7a3b
...
@@ -3,17 +3,18 @@
...
@@ -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(){
...
@@ -88,7 +89,7 @@ $(document).ready(function(){
};
};
var
table
=
jumpserver
.
initDataTable
(
options
);
var
table
=
jumpserver
.
initDataTable
(
options
);
$
(
'.b
uttons-csv
'
).
click
(
function
()
{
$
(
'.b
tn_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(){
...
@@ -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(){
...
@@ -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(){
...
@@ -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 %}
apps/users/urls/views_urls.py
View file @
e28f7a3b
...
@@ -24,7 +24,7 @@ urlpatterns = [
...
@@ -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'
),
...
...
apps/users/views.py
View file @
e28f7a3b
...
@@ -5,11 +5,11 @@ import json
...
@@ -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):
...
@@ -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):
...
@@ -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
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment