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
cf15b7ea
Commit
cf15b7ea
authored
Aug 18, 2016
by
ibuler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add UserGroup some View
parent
651e8999
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
289 additions
and
16 deletions
+289
-16
chosen.css
apps/static/css/plugins/chosen/chosen.css
+0
-0
style.css
apps/static/css/style.css
+3
-1
_nav.html
apps/templates/_nav.html
+1
-1
forms.py
apps/users/forms.py
+7
-0
models.py
apps/users/models.py
+50
-2
usergroup_add.html
apps/users/templates/users/usergroup_add.html
+76
-0
usergroup_list.html
apps/users/templates/users/usergroup_list.html
+81
-0
urls.py
apps/users/urls.py
+11
-5
views.py
apps/users/views.py
+60
-7
No files found.
apps/static/css/plugins/chosen/chosen.css
View file @
cf15b7ea
This diff is collapsed.
Click to expand it.
apps/static/css/style.css
View file @
cf15b7ea
...
@@ -4600,4 +4600,6 @@ body.skin-3 {
...
@@ -4600,4 +4600,6 @@ body.skin-3 {
border-width
:
1px
border-width
:
1px
}
}
th
a
{
color
:
#676a6c
;
}
apps/templates/_nav.html
View file @
cf15b7ea
...
@@ -9,7 +9,7 @@
...
@@ -9,7 +9,7 @@
</a>
</a>
<ul
class=
"nav nav-second-level"
>
<ul
class=
"nav nav-second-level"
>
<li
class=
"group"
><a
href=
"{% url 'users:user-list' %}"
>
用户列表
</a></li>
<li
class=
"group"
><a
href=
"{% url 'users:user-list' %}"
>
用户列表
</a></li>
<li
class=
"user"
><a
href=
"
#
"
>
用户组列表
</a></li>
<li
class=
"user"
><a
href=
"
{% url 'users:usergroup-list' %}
"
>
用户组列表
</a></li>
</ul>
</ul>
</li>
</li>
<li
id=
""
>
<li
id=
""
>
...
...
apps/users/forms.py
View file @
cf15b7ea
...
@@ -26,3 +26,10 @@ class UserUpdateForm(ModelForm):
...
@@ -26,3 +26,10 @@ class UserUpdateForm(ModelForm):
'phone'
,
'enable_2FA'
,
'role'
,
'date_expired'
,
'comment'
,
'phone'
,
'enable_2FA'
,
'role'
,
'date_expired'
,
'comment'
,
]
]
class
UserGroupForm
(
ModelForm
):
class
Meta
:
model
=
UserGroup
fields
=
[
'name'
,
'comment'
,
]
apps/users/models.py
View file @
cf15b7ea
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
from
__future__
import
unicode_literals
from
__future__
import
unicode_literals
import
datetime
import
datetime
from
django.utils
import
timezone
from
django.db
import
models
from
django.db
import
models
from
django.contrib.auth.models
import
AbstractUser
,
Permission
from
django.contrib.auth.models
import
AbstractUser
,
Permission
from
django.contrib.auth.models
import
Group
as
AbstractGroup
from
django.contrib.auth.models
import
Group
as
AbstractGroup
...
@@ -55,6 +55,24 @@ class UserGroup(models.Model):
...
@@ -55,6 +55,24 @@ class UserGroup(models.Model):
group
=
cls
(
name
=
'所有人'
,
comment
=
'所有人默认都在用户组'
,
created_by
=
'System'
)
group
=
cls
(
name
=
'所有人'
,
comment
=
'所有人默认都在用户组'
,
created_by
=
'System'
)
group
.
save
()
group
.
save
()
@classmethod
def
generate_fake
(
cls
,
count
=
100
):
from
random
import
seed
,
randint
,
choice
import
forgery_py
from
django.db
import
IntegrityError
seed
()
for
i
in
range
(
count
):
group
=
cls
(
name
=
forgery_py
.
name
.
full_name
(),
comment
=
forgery_py
.
lorem_ipsum
.
sentence
(),
created_by
=
choice
(
User
.
objects
.
all
())
.
username
)
try
:
group
.
save
()
except
IntegrityError
:
print
(
'Error continue'
)
continue
class
User
(
AbstractUser
):
class
User
(
AbstractUser
):
username
=
models
.
CharField
(
max_length
=
20
,
unique
=
True
,
verbose_name
=
'用户名'
,
help_text
=
'* required'
)
username
=
models
.
CharField
(
max_length
=
20
,
unique
=
True
,
verbose_name
=
'用户名'
,
help_text
=
'* required'
)
...
@@ -71,7 +89,37 @@ class User(AbstractUser):
...
@@ -71,7 +89,37 @@ class User(AbstractUser):
public_key
=
models
.
CharField
(
max_length
=
1000
,
blank
=
True
,
verbose_name
=
'公钥'
)
public_key
=
models
.
CharField
(
max_length
=
1000
,
blank
=
True
,
verbose_name
=
'公钥'
)
comment
=
models
.
TextField
(
max_length
=
200
,
blank
=
True
,
verbose_name
=
'描述'
)
comment
=
models
.
TextField
(
max_length
=
200
,
blank
=
True
,
verbose_name
=
'描述'
)
created_by
=
models
.
CharField
(
max_length
=
30
,
default
=
''
)
created_by
=
models
.
CharField
(
max_length
=
30
,
default
=
''
)
date_expired
=
models
.
DateTimeField
(
default
=
datetime
.
datetime
.
max
,
verbose_name
=
'有效期'
)
date_expired
=
models
.
DateTimeField
(
default
=
timezone
.
now
()
+
timezone
.
timedelta
(
days
=
365
*
70
)
,
verbose_name
=
'有效期'
)
class
Meta
:
class
Meta
:
db_table
=
'user'
db_table
=
'user'
@classmethod
def
generate_fake
(
cls
,
count
=
100
):
from
random
import
seed
,
randint
,
choice
import
forgery_py
from
django.contrib.auth.hashers
import
make_password
from
django.db
import
IntegrityError
seed
()
for
i
in
range
(
count
):
user
=
cls
(
username
=
forgery_py
.
internet
.
user_name
(
True
),
email
=
forgery_py
.
internet
.
email_address
(),
name
=
forgery_py
.
name
.
full_name
(),
password
=
make_password
(
forgery_py
.
lorem_ipsum
.
word
()),
role
=
choice
(
Role
.
objects
.
all
()),
wechat
=
forgery_py
.
internet
.
user_name
(
True
),
comment
=
forgery_py
.
lorem_ipsum
.
sentence
(),
created_by
=
choice
(
cls
.
objects
.
all
())
.
username
,
)
try
:
user
.
save
()
except
IntegrityError
:
print
(
'Error continue'
)
continue
user
.
groups
.
add
(
choice
(
UserGroup
.
objects
.
all
()))
user
.
save
()
apps/users/templates/users/usergroup_add.html
0 → 100644
View file @
cf15b7ea
{% extends 'base.html' %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link
href=
"{% static "
css
/
plugins
/
chosen
/
chosen
.
css
"
%}"
rel=
"stylesheet"
>
<script
src=
"{% static "
js
/
plugins
/
chosen
/
chosen
.
jquery
.
min
.
js
"
%}"
></script>
{% endblock %}
{% block content %}
<div
class=
"wrapper wrapper-content animated fadeInRight"
>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<div
class=
"ibox float-e-margins"
>
<div
class=
"ibox-title"
>
<h5>
填写用户组信息
</h5>
<div
class=
"ibox-tools"
>
<a
class=
"collapse-link"
>
<i
class=
"fa fa-chevron-up"
></i>
</a>
<a
class=
"dropdown-toggle"
data-toggle=
"dropdown"
href=
"#"
>
<i
class=
"fa fa-wrench"
></i>
</a>
<a
class=
"close-link"
>
<i
class=
"fa fa-times"
></i>
</a>
</div>
</div>
<div
class=
"ibox-content"
>
<form
method=
"post"
id=
"userForm"
class=
"form-horizontal"
action=
""
>
{% csrf_token %}
{{ form.name|bootstrap_horizontal }}
<div
class=
"form-group"
>
<label
for=
"users"
class=
"col-sm-2 control-label"
>
用户
</label>
<div
class=
"col-sm-8"
>
<select
name=
"users"
id=
"users"
data-placeholder=
"选择用户"
class=
"chosen-select form-control m-b"
multiple
tabindex=
"2"
>
{% for user in users %}
<option
value=
"{{ user.id }}"
>
{{ user.name }}
</option>
{% endfor %}
</select>
<span
class=
"help-block m-b-none"
>
用户和用户组必选一个
</span>
</div>
</div>
{{ form.comment|bootstrap_horizontal }}
<div
class=
"form-group"
>
<div
class=
"col-sm-4 col-sm-offset-2"
>
<button
class=
"btn btn-white"
type=
"reset"
>
取消
</button>
<button
id=
"submit_button"
class=
"btn btn-primary"
type=
"submit"
>
确认保存
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$
(
document
).
ready
(
function
()
{
var
config
=
{
'.chosen-select'
:
{},
'.chosen-select-deselect'
:
{
allow_single_deselect
:
true
},
'.chosen-select-no-single'
:
{
disable_search_threshold
:
10
},
'.chosen-select-no-results'
:
{
no_results_text
:
'Oops, nothing found!'
},
'.chosen-select-width'
:
{
width
:
"95%"
}
};
for
(
var
selector
in
config
)
{
$
(
selector
).
chosen
(
config
[
selector
]);
}
})
</script>
{% endblock %}
\ No newline at end of file
apps/users/templates/users/usergroup_list.html
0 → 100644
View file @
cf15b7ea
{% extends 'base.html' %}
{% load common_tags %}
{% block content %}
<div
class=
"wrapper wrapper-content animated fadeInRight"
>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<div
class=
"ibox float-e-margins"
>
<div
class=
"ibox-title"
>
<h5>
查看用户组
</h5>
<div
class=
"ibox-tools"
>
<a
class=
"collapise-link"
>
<i
class=
"fa fa-chevron-up"
></i>
</a>
<a
class=
"dropdown-toggle"
data-toggle=
"dropdown"
href=
"#"
>
<i
class=
"fa fa-wrench"
></i>
</a>
<a
class=
"close-link"
>
<i
class=
"fa fa-times"
></i>
</a>
</div>
</div>
<div
class=
"ibox-content"
>
<div
class=
""
>
<a
href=
"{% url 'users:usergroup-add' %}"
class=
"btn btn-sm btn-primary "
>
添加用户组
</a>
<a
id=
"del_btn"
class=
"btn btn-sm btn-danger "
>
删除所选
</a>
<form
id=
"search_form"
method=
"get"
action=
"{% url 'users:user-list' %}"
class=
"pull-right mail-search"
>
<div
class=
"input-group"
>
<input
type=
"text"
class=
"form-control input-sm"
name=
"keyword"
placeholder=
"名称"
value=
"{{ keyword }}"
>
<div
class=
"input-group-btn"
>
<button
id=
'search_btn'
type=
"submit"
class=
"btn btn-sm btn-primary"
>
搜索
</button>
</div>
</div>
</form>
</div>
<table
class=
"table table-striped table-bordered table-hover "
id=
"editable"
>
<thead>
<tr>
<th
class=
"text-center"
>
<input
type=
"checkbox"
id=
"check_all"
onclick=
"checkAll('check_all', 'checked')"
>
</th>
<th
class=
"text-center"
><a
href=
"{% url 'users:usergroup-list' %}?sort=name"
>
名称
</a></th>
<th
class=
"text-center"
>
用户数量
</th>
<th
class=
"text-center"
>
资产数量
</th>
<th
class=
"text-center"
>
描述
</th>
<th
class=
"text-center"
></th>
</tr>
</thead>
<tbody>
{% for usergroup in usergroup_list %}
<tr
class=
"gradeX"
>
<td
class=
"text-center"
>
<input
type=
"checkbox"
name=
"checked"
value=
"{{ usergroup.id }}"
>
</td>
<td
class=
"text-center"
>
<a
href=
"{% url 'users:usergroup-detail' pk=usergroup.id %}"
>
{{ usergroup.name }}
</a>
</td>
<td
class=
"text-center"
>
{{ usergroup.user_set.all|length }}
</td>
<td
class=
"text-center"
>
数量
</td>
<th
class=
"text-center"
>
{{ usergroup.comment|truncatewords:8 }}
</th>
<td
class=
"text-center"
>
<a
href=
"{% url 'users:usergroup-edit' pk=usergroup.id %}?id={{ user.id }}"
class=
"btn btn-xs btn-info"
>
编辑
</a>
<a
href=
"{% url 'users:usergroup-delete' pk=usergroup.id %}?id={{ user.id }}"
class=
"btn btn-xs btn-danger del"
>
删除
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% include '_pagination.html' %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
apps/users/urls.py
View file @
cf15b7ea
from
django.conf.urls
import
url
from
django.conf.urls
import
url
from
.views
import
UserListView
,
UserAddView
,
UserUpdateView
,
UserDeleteView
,
UserDetailView
from
.views
import
UserListView
,
UserAddView
,
UserUpdateView
,
UserDeleteView
,
UserDetailView
from
.views
import
UserGroupListView
,
UserGroupAddView
,
UserGroupUpdateView
,
UserGroupDeleteView
,
UserGroupDetailView
app_name
=
'users'
app_name
=
'users'
urlpatterns
=
[
urlpatterns
=
[
url
(
r'^$'
,
UserListView
.
as_view
(),
name
=
'user-list'
),
url
(
r'^user/$'
,
UserListView
.
as_view
(),
name
=
'user-list'
),
url
(
r'^(?P<pk>[0-9]+)/$'
,
UserDetailView
.
as_view
(),
name
=
'user-detail'
),
url
(
r'^user/(?P<pk>[0-9]+)/$'
,
UserDetailView
.
as_view
(),
name
=
'user-detail'
),
url
(
r'^add/$'
,
UserAddView
.
as_view
(),
name
=
'user-add'
),
url
(
r'^user/add/$'
,
UserAddView
.
as_view
(),
name
=
'user-add'
),
url
(
r'^(?P<pk>[0-9]+)/edit/$'
,
UserUpdateView
.
as_view
(),
name
=
'user-edit'
),
url
(
r'^user/(?P<pk>[0-9]+)/edit/$'
,
UserUpdateView
.
as_view
(),
name
=
'user-edit'
),
url
(
r'^(?P<pk>[0-9]+)/delete/$'
,
UserDeleteView
.
as_view
(),
name
=
'user-delete'
),
url
(
r'^user/(?P<pk>[0-9]+)/delete/$'
,
UserDeleteView
.
as_view
(),
name
=
'user-delete'
),
url
(
r'^usergroup/$'
,
UserGroupListView
.
as_view
(),
name
=
'usergroup-list'
),
url
(
r'^usergroup/(?P<pk>[0-9]+)/$'
,
UserGroupDetailView
.
as_view
(),
name
=
'usergroup-detail'
),
url
(
r'^usergroup/add/$'
,
UserGroupAddView
.
as_view
(),
name
=
'usergroup-add'
),
url
(
r'^usergroup/(?P<pk>[0-9]+)/edit/$'
,
UserGroupUpdateView
.
as_view
(),
name
=
'usergroup-edit'
),
url
(
r'^usergroup/(?P<pk>[0-9]+)/delete/$'
,
UserGroupDeleteView
.
as_view
(),
name
=
'usergroup-delete'
),
]
]
apps/users/views.py
View file @
cf15b7ea
# ~*~ coding: utf-8 ~*~
# ~*~ coding: utf-8 ~*~
from
django.shortcuts
import
get_object_or_404
from
django.urls
import
reverse_lazy
from
django.urls
import
reverse_lazy
from
django.db.models
import
Q
from
django.db.models
import
Q
from
django.views.generic.list
import
ListView
from
django.views.generic.list
import
ListView
...
@@ -7,7 +8,7 @@ from django.views.generic.edit import CreateView, DeleteView, UpdateView
...
@@ -7,7 +8,7 @@ from django.views.generic.edit import CreateView, DeleteView, UpdateView
from
django.views.generic.detail
import
DetailView
from
django.views.generic.detail
import
DetailView
from
.models
import
User
,
UserGroup
,
Role
from
.models
import
User
,
UserGroup
,
Role
from
.forms
import
UserAddForm
,
UserUpdateForm
from
.forms
import
UserAddForm
,
UserUpdateForm
,
UserGroupForm
class
UserListView
(
ListView
):
class
UserListView
(
ListView
):
...
@@ -71,12 +72,6 @@ class UserUpdateView(UpdateView):
...
@@ -71,12 +72,6 @@ class UserUpdateView(UpdateView):
user
.
set_password
(
password
)
user
.
set_password
(
password
)
return
super
(
UserUpdateView
,
self
)
.
form_valid
(
form
)
return
super
(
UserUpdateView
,
self
)
.
form_valid
(
form
)
def
form_invalid
(
self
,
form
):
print
(
self
.
request
.
FILES
)
print
(
form
[
'avatar'
]
.
value
())
print
(
form
.
errors
)
return
super
(
UserUpdateView
,
self
)
.
form_invalid
(
form
)
class
UserDeleteView
(
DeleteView
):
class
UserDeleteView
(
DeleteView
):
model
=
User
model
=
User
...
@@ -94,3 +89,61 @@ class UserDetailView(DetailView):
...
@@ -94,3 +89,61 @@ class UserDetailView(DetailView):
groups
=
[
group
for
group
in
UserGroup
.
objects
.
iterator
()
if
group
not
in
self
.
object
.
groups
.
iterator
()]
groups
=
[
group
for
group
in
UserGroup
.
objects
.
iterator
()
if
group
not
in
self
.
object
.
groups
.
iterator
()]
context
.
update
({
'path1'
:
'用户管理'
,
'path2'
:
'用户详情'
,
'title'
:
'用户详情'
,
'groups'
:
groups
})
context
.
update
({
'path1'
:
'用户管理'
,
'path2'
:
'用户详情'
,
'title'
:
'用户详情'
,
'groups'
:
groups
})
return
context
return
context
class
UserGroupListView
(
ListView
):
model
=
UserGroup
paginate_by
=
20
context_object_name
=
'usergroup_list'
template_name
=
'users/usergroup_list.html'
ordering
=
'-date_added'
def
get_queryset
(
self
):
self
.
queryset
=
super
(
UserGroupListView
,
self
)
.
get_queryset
()
self
.
keyword
=
keyword
=
self
.
request
.
GET
.
get
(
'keyword'
,
''
)
self
.
sort
=
sort
=
self
.
request
.
GET
.
get
(
'sort'
)
if
keyword
:
self
.
queryset
=
self
.
queryset
.
filter
(
name__icontains
=
keyword
)
if
sort
:
self
.
queryset
=
self
.
queryset
.
order_by
(
sort
)
return
self
.
queryset
def
get_context_data
(
self
,
**
kwargs
):
context
=
super
(
UserGroupListView
,
self
)
.
get_context_data
(
**
kwargs
)
context
.
update
({
'path1'
:
'用户管理'
,
'path2'
:
'用户组列表'
,
'title'
:
'用户组列表'
,
'keyword'
:
self
.
keyword
})
return
context
class
UserGroupAddView
(
CreateView
):
model
=
UserGroup
form_class
=
UserGroupForm
template_name
=
'users/usergroup_add.html'
success_url
=
reverse_lazy
(
'users:usergroup-list'
)
def
get_context_data
(
self
,
**
kwargs
):
context
=
super
(
UserGroupAddView
,
self
)
.
get_context_data
(
**
kwargs
)
users
=
User
.
objects
.
all
()
context
.
update
({
'path1'
:
'用户管理'
,
'path2'
:
'用户组添加'
,
'title'
:
'用户组添加'
,
'users'
:
users
})
return
context
def
form_valid
(
self
,
form
):
usergroup
=
form
.
save
()
users_id_list
=
self
.
request
.
POST
.
getlist
(
'users'
,
[])
users
=
[
get_object_or_404
(
User
,
id
=
user_id
)
for
user_id
in
users_id_list
]
usergroup
.
created_by
=
self
.
request
.
user
.
username
or
'Admin'
usergroup
.
user_set
.
add
(
*
tuple
(
users
))
usergroup
.
save
()
return
super
(
UserGroupAddView
,
self
)
.
form_valid
(
form
)
class
UserGroupUpdateView
(
UpdateView
):
pass
class
UserGroupDetailView
(
DetailView
):
pass
class
UserGroupDeleteView
(
DeleteView
):
pass
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