mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-07-13 14:44:47 +00:00
Merge branch 'master' of code.simcu.com:jumpserver/jumpserver
This commit is contained in:
commit
7cebc4efe8
7
apps/__init__.py
Normal file
7
apps/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
Binary file not shown.
Before Width: | Height: | Size: 646 B |
Binary file not shown.
Before Width: | Height: | Size: 872 B |
@ -1,429 +0,0 @@
|
||||
/*!
|
||||
Chosen, a Select Box Enhancer for jQuery and Prototype
|
||||
by Patrick Filler for Harvest, http://getharvest.com
|
||||
|
||||
Version 1.1.0
|
||||
Full source at https://github.com/harvesthq/chosen
|
||||
Copyright (c) 2011 Harvest http://getharvest.com
|
||||
|
||||
MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
|
||||
This file is generated by `grunt build`, do not edit it by hand.
|
||||
*/
|
||||
|
||||
/* @group Base */
|
||||
.chosen-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
font-size: 13px;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.chosen-container .chosen-drop {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: -9999px;
|
||||
z-index: 1010;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
border: 1px solid #aaa;
|
||||
border-top: 0;
|
||||
background: #fff;
|
||||
box-shadow: 0 4px 5px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.chosen-container.chosen-with-drop .chosen-drop {
|
||||
left: 0;
|
||||
}
|
||||
.chosen-container a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* @end */
|
||||
/* @group Single Chosen */
|
||||
.chosen-container-single .chosen-single {
|
||||
position: relative;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
padding: 0 0 0 8px;
|
||||
height: 23px;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 5px;
|
||||
background-color: #fff;
|
||||
background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4));
|
||||
background: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
|
||||
background: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
|
||||
background: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
|
||||
background: linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
|
||||
background-clip: padding-box;
|
||||
box-shadow: 0 0 3px white inset, 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
color: #444;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
line-height: 24px;
|
||||
}
|
||||
.chosen-container-single .chosen-default {
|
||||
color: #999;
|
||||
}
|
||||
.chosen-container-single .chosen-single span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin-right: 26px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chosen-container-single .chosen-single-with-deselect span {
|
||||
margin-right: 38px;
|
||||
}
|
||||
.chosen-container-single .chosen-single abbr {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 26px;
|
||||
display: block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: url('chosen-sprite.png') -42px 1px no-repeat;
|
||||
font-size: 1px;
|
||||
}
|
||||
.chosen-container-single .chosen-single abbr:hover {
|
||||
background-position: -42px -10px;
|
||||
}
|
||||
.chosen-container-single.chosen-disabled .chosen-single abbr:hover {
|
||||
background-position: -42px -10px;
|
||||
}
|
||||
.chosen-container-single .chosen-single div {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: block;
|
||||
width: 18px;
|
||||
height: 100%;
|
||||
}
|
||||
.chosen-container-single .chosen-single div b {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: url('chosen-sprite.png') no-repeat 0px 2px;
|
||||
}
|
||||
.chosen-container-single .chosen-search {
|
||||
position: relative;
|
||||
z-index: 1010;
|
||||
margin: 0;
|
||||
padding: 3px 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chosen-container-single .chosen-search input[type="text"] {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
margin: 1px 0;
|
||||
padding: 4px 20px 4px 5px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
outline: 0;
|
||||
border: 1px solid #aaa;
|
||||
background: white url('chosen-sprite.png') no-repeat 100% -20px;
|
||||
background: url('chosen-sprite.png') no-repeat 100% -20px;
|
||||
font-size: 1em;
|
||||
font-family: sans-serif;
|
||||
line-height: normal;
|
||||
border-radius: 0;
|
||||
}
|
||||
.chosen-container-single .chosen-drop {
|
||||
margin-top: -1px;
|
||||
border-radius: 0 0 4px 4px;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
.chosen-container-single.chosen-container-single-nosearch .chosen-search {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
}
|
||||
|
||||
/* @end */
|
||||
/* @group Results */
|
||||
.chosen-container .chosen-results {
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
margin: 0 4px 4px 0;
|
||||
padding: 0 0 0 4px;
|
||||
max-height: 240px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
.chosen-container .chosen-results li {
|
||||
display: none;
|
||||
margin: 0;
|
||||
padding: 5px 6px;
|
||||
list-style: none;
|
||||
line-height: 15px;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
.chosen-container .chosen-results li.active-result {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
.chosen-container .chosen-results li.disabled-result {
|
||||
display: list-item;
|
||||
color: #ccc;
|
||||
cursor: default;
|
||||
}
|
||||
.chosen-container .chosen-results li.highlighted {
|
||||
background-color: #3875d7;
|
||||
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc));
|
||||
background-image: -webkit-linear-gradient(#3875d7 20%, #2a62bc 90%);
|
||||
background-image: -moz-linear-gradient(#3875d7 20%, #2a62bc 90%);
|
||||
background-image: -o-linear-gradient(#3875d7 20%, #2a62bc 90%);
|
||||
background-image: linear-gradient(#3875d7 20%, #2a62bc 90%);
|
||||
color: #fff;
|
||||
}
|
||||
.chosen-container .chosen-results li.no-results {
|
||||
display: list-item;
|
||||
background: #f4f4f4;
|
||||
}
|
||||
.chosen-container .chosen-results li.group-result {
|
||||
display: list-item;
|
||||
font-weight: bold;
|
||||
cursor: default;
|
||||
}
|
||||
.chosen-container .chosen-results li.group-option {
|
||||
padding-left: 15px;
|
||||
}
|
||||
.chosen-container .chosen-results li em {
|
||||
font-style: normal;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* @end */
|
||||
/* @group Multi Chosen */
|
||||
.chosen-container-multi .chosen-choices {
|
||||
-moz-box-sizing: border-box;
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid #CBD5DD;
|
||||
border-radius: 2px;
|
||||
cursor: text;
|
||||
height: auto !important;
|
||||
margin: 0;
|
||||
min-height: 30px;
|
||||
overflow: hidden;
|
||||
padding: 2px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.chosen-container-multi .chosen-choices li {
|
||||
float: left;
|
||||
list-style: none;
|
||||
}
|
||||
.chosen-container-multi .chosen-choices li.search-field {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chosen-container-multi .chosen-choices li.search-field input[type="text"] {
|
||||
margin: 1px 0;
|
||||
padding: 5px;
|
||||
height: 25px;
|
||||
outline: 0;
|
||||
border: 0 !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none;
|
||||
color: #666;
|
||||
font-size: 100%;
|
||||
font-family: sans-serif;
|
||||
line-height: normal;
|
||||
border-radius: 0;
|
||||
}
|
||||
.chosen-container-multi .chosen-choices li.search-field .default {
|
||||
color: #999;
|
||||
}
|
||||
.chosen-container-multi .chosen-choices li.search-choice {
|
||||
position: relative;
|
||||
margin: 3px 0 3px 5px;
|
||||
padding: 3px 20px 3px 5px;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 3px;
|
||||
background-color: #e4e4e4;
|
||||
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
|
||||
background-image: -webkit-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||
background-image: -moz-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||
background-image: -o-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||
background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||
background-clip: padding-box;
|
||||
box-shadow: 0 0 2px white inset, 0 1px 0 rgba(0, 0, 0, 0.05);
|
||||
color: #333;
|
||||
line-height: 13px;
|
||||
cursor: default;
|
||||
}
|
||||
.chosen-container-multi .chosen-choices li.search-choice .search-choice-close {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 3px;
|
||||
display: block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: url('chosen-sprite.png') -42px 1px no-repeat;
|
||||
font-size: 1px;
|
||||
}
|
||||
.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover {
|
||||
background-position: -42px -10px;
|
||||
}
|
||||
.chosen-container-multi .chosen-choices li.search-choice-disabled {
|
||||
padding-right: 5px;
|
||||
border: 1px solid #ccc;
|
||||
background-color: #e4e4e4;
|
||||
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
|
||||
background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||
background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||
background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||
background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||
color: #666;
|
||||
}
|
||||
.chosen-container-multi .chosen-choices li.search-choice-focus {
|
||||
background: #d4d4d4;
|
||||
}
|
||||
.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close {
|
||||
background-position: -42px -10px;
|
||||
}
|
||||
.chosen-container-multi .chosen-results {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.chosen-container-multi .chosen-drop .result-selected {
|
||||
display: list-item;
|
||||
color: #ccc;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* @end */
|
||||
/* @group Active */
|
||||
.chosen-container-active .chosen-single {
|
||||
border: 1px solid #5897fb;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.chosen-container-active.chosen-with-drop .chosen-single {
|
||||
border: 1px solid #aaa;
|
||||
-moz-border-radius-bottomright: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
-moz-border-radius-bottomleft: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff));
|
||||
background-image: -webkit-linear-gradient(#eeeeee 20%, #ffffff 80%);
|
||||
background-image: -moz-linear-gradient(#eeeeee 20%, #ffffff 80%);
|
||||
background-image: -o-linear-gradient(#eeeeee 20%, #ffffff 80%);
|
||||
background-image: linear-gradient(#eeeeee 20%, #ffffff 80%);
|
||||
box-shadow: 0 1px 0 #fff inset;
|
||||
}
|
||||
.chosen-container-active.chosen-with-drop .chosen-single div {
|
||||
border-left: none;
|
||||
background: transparent;
|
||||
}
|
||||
.chosen-container-active.chosen-with-drop .chosen-single div b {
|
||||
background-position: -18px 2px;
|
||||
}
|
||||
.chosen-container-active .chosen-choices {
|
||||
border: 1px solid #5897fb;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.chosen-container-active .chosen-choices li.search-field input[type="text"] {
|
||||
color: #111 !important;
|
||||
}
|
||||
|
||||
/* @end */
|
||||
/* @group Disabled Support */
|
||||
.chosen-disabled {
|
||||
opacity: 0.5 !important;
|
||||
cursor: default;
|
||||
}
|
||||
.chosen-disabled .chosen-single {
|
||||
cursor: default;
|
||||
}
|
||||
.chosen-disabled .chosen-choices .search-choice .search-choice-close {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* @end */
|
||||
/* @group Right to Left */
|
||||
.chosen-rtl {
|
||||
text-align: right;
|
||||
}
|
||||
.chosen-rtl .chosen-single {
|
||||
overflow: visible;
|
||||
padding: 0 8px 0 0;
|
||||
}
|
||||
.chosen-rtl .chosen-single span {
|
||||
margin-right: 0;
|
||||
margin-left: 26px;
|
||||
direction: rtl;
|
||||
}
|
||||
.chosen-rtl .chosen-single-with-deselect span {
|
||||
margin-left: 38px;
|
||||
}
|
||||
.chosen-rtl .chosen-single div {
|
||||
right: auto;
|
||||
left: 3px;
|
||||
}
|
||||
.chosen-rtl .chosen-single abbr {
|
||||
right: auto;
|
||||
left: 26px;
|
||||
}
|
||||
.chosen-rtl .chosen-choices li {
|
||||
float: right;
|
||||
}
|
||||
.chosen-rtl .chosen-choices li.search-field input[type="text"] {
|
||||
direction: rtl;
|
||||
}
|
||||
.chosen-rtl .chosen-choices li.search-choice {
|
||||
margin: 3px 5px 3px 0;
|
||||
padding: 3px 5px 3px 19px;
|
||||
}
|
||||
.chosen-rtl .chosen-choices li.search-choice .search-choice-close {
|
||||
right: auto;
|
||||
left: 4px;
|
||||
}
|
||||
.chosen-rtl.chosen-container-single-nosearch .chosen-search,
|
||||
.chosen-rtl .chosen-drop {
|
||||
left: 9999px;
|
||||
}
|
||||
.chosen-rtl.chosen-container-single .chosen-results {
|
||||
margin: 0 0 4px 4px;
|
||||
padding: 0 4px 0 0;
|
||||
}
|
||||
.chosen-rtl .chosen-results li.group-option {
|
||||
padding-right: 15px;
|
||||
padding-left: 0;
|
||||
}
|
||||
.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div {
|
||||
border-right: none;
|
||||
}
|
||||
.chosen-rtl .chosen-search input[type="text"] {
|
||||
padding: 4px 5px 4px 20px;
|
||||
background: white url('chosen-sprite.png') no-repeat -30px -20px;
|
||||
background: url('chosen-sprite.png') no-repeat -30px -20px;
|
||||
direction: rtl;
|
||||
}
|
||||
.chosen-rtl.chosen-container-single .chosen-single div b {
|
||||
background-position: 6px 2px;
|
||||
}
|
||||
.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b {
|
||||
background-position: -12px 2px;
|
||||
}
|
||||
|
||||
/* @end */
|
||||
/* @group Retina compatibility */
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-resolution: 144dpi) {
|
||||
.chosen-rtl .chosen-search input[type="text"],
|
||||
.chosen-container-single .chosen-single abbr,
|
||||
.chosen-container-single .chosen-single div b,
|
||||
.chosen-container-single .chosen-search input[type="text"],
|
||||
.chosen-container-multi .chosen-choices .search-choice .search-choice-close,
|
||||
.chosen-container .chosen-results-scroll-down span,
|
||||
.chosen-container .chosen-results-scroll-up span {
|
||||
background-image: url('chosen-sprite@2x.png') !important;
|
||||
background-size: 52px 37px !important;
|
||||
background-repeat: no-repeat !important;
|
||||
}
|
||||
}
|
||||
/* @end */
|
1
apps/static/css/plugins/select2/select2.min.css
vendored
Executable file
1
apps/static/css/plugins/select2/select2.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
@ -969,17 +969,43 @@ button.dim:active:before {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-results__option--highlighted[aria-selected] {
|
||||
background-color: #1ab394;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.select2-selection--multiple {
|
||||
border: 1px solid #e5e6e7 !important;
|
||||
cursor: text !important;
|
||||
}
|
||||
|
||||
/*.select2-container--classic .select2-selection--multiple:focus {*/
|
||||
/*border: 1px solid #1ab394;*/
|
||||
/*}*/
|
||||
|
||||
.select2-container--forcus {
|
||||
border: 1px solid #1AB394 !important;
|
||||
}
|
||||
|
||||
.select2-selection__choice,
|
||||
.chosen-container-multi .chosen-choices li.search-choice {
|
||||
background: #f1f1f1;
|
||||
border: 1px solid #ededed;
|
||||
border-radius: 2px;
|
||||
box-shadow: none;
|
||||
color: #333333;
|
||||
cursor: default;
|
||||
line-height: 13px;
|
||||
margin: 3px 0 3px 5px;
|
||||
padding: 3px 20px 3px 5px;
|
||||
position: relative;
|
||||
background: #f1f1f1 !important;
|
||||
border: 1px solid #e5e6e7 !important;
|
||||
/*border: 1px solid #ededed;*/
|
||||
border-radius: 2px !important;
|
||||
box-shadow: none !important;
|
||||
color: #333333 !important;
|
||||
cursor: default !important;
|
||||
line-height: 13px !important;
|
||||
/*margin: 3px 0 3px 5px !important;*/
|
||||
padding: 3px 20px 3px 5px !important;
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.select2-container--default.select2-container--focus .select2-selection--multiple {
|
||||
border: 1px solid #1ab394 !important;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3) !important;
|
||||
}
|
||||
|
||||
.chosen-container .chosen-results li.highlighted {
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
3
apps/static/js/plugins/select2/select2.full.min.js
vendored
Executable file
3
apps/static/js/plugins/select2/select2.full.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
@ -1,29 +1,11 @@
|
||||
{% load static %}
|
||||
<!-- Mainly scripts -->
|
||||
<script src="{% static "js/plugins/metisMenu/jquery.metisMenu.js" %}"></script>
|
||||
{#<script src="{% static "js/plugins/slimscroll/jquery.slimscroll.min.js" %}"></script>#}
|
||||
{#<script src="{% static "js/bootstrap-dialog.js" %}"></script>#}
|
||||
{#<script src="{% static "js/mindmup-editabletable.js" %}"></script>#}
|
||||
{#<script src="{% static "js/plugins/fullcalendar/moment.min.js" %}"></script>#}
|
||||
{#<script src="{% static "js/plugins/fullcalendar/fullcalendar.min.js" %}"></script>#}
|
||||
|
||||
<!-- Custom and plugin javascript -->
|
||||
<script src="{% static "js/inspinia.js" %}"></script>
|
||||
{#<script src="{% static "js/plugins/pace/pace.min.js" %}"></script>#}
|
||||
|
||||
<!-- Peity -->
|
||||
{#<script src="{% static "js/plugins/peity/jquery.peity.min.js" %}"></script>#}
|
||||
{#<script src="{% static "js/demo/peity-demo.js" %}"></script>#}
|
||||
|
||||
<script src="{% static "js/base.js" %}"></script>
|
||||
|
||||
<!-- pop windows layer-->
|
||||
{#<script src="{% static "js/layer/layer.js" %}"></script>#}
|
||||
|
||||
<!-- highcharts -->
|
||||
{#<script src="{% static "js/highcharts/highcharts.js" %}"></script>#}
|
||||
|
||||
{#<script src="{% static "js/dropzone/dropzone.js" %}"></script>#}
|
||||
<!-- active menu -->
|
||||
<script>
|
||||
var url_array = document.location.pathname.split("/");
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="row border-bottom">
|
||||
<nav class="navbar navbar-static-top" role="navigation" style="margin-bottom: 0">
|
||||
<nav class="navbar navbar-static-top white-bg" role="navigation" style="margin-bottom: 0">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-minimalize minimalize-styl-2 btn btn-primary " href="#"><i class="fa fa-bars"></i> </a>
|
||||
<form role="search" class="navbar-form-custom" method="get" action="">
|
||||
|
62
apps/templates/_list_base.html
Normal file
62
apps/templates/_list_base.html
Normal file
@ -0,0 +1,62 @@
|
||||
{% 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> {{ path2 }} </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="">
|
||||
{# left button add #}
|
||||
{% block content_left_head %} {% endblock %}
|
||||
<form id="search_form" method="get" action="" class="pull-right mail-search">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control input-sm" name="keyword" placeholder="Search" 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>
|
||||
{% block table_head %} {% endblock %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% block table_body %} {% endblock %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
{# Update batch #}
|
||||
{% block content_bottom_left %} {% endblock %}
|
||||
</div>
|
||||
{% include '_pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
7
apps/templates/_message.html
Normal file
7
apps/templates/_message.html
Normal file
@ -0,0 +1,7 @@
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ message.tags }}" style="margin: 20px auto 0px">
|
||||
{{ message|safe }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
@ -1,15 +1,15 @@
|
||||
<li id="">
|
||||
<li id="index">
|
||||
<a href="">
|
||||
<i class="fa fa-dashboard"></i> <span class="nav-label">仪表盘</span><span class="label label-info pull-right"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li id="">
|
||||
<li id="users">
|
||||
<a href="#">
|
||||
<i class="fa fa-group"></i> <span class="nav-label">用户管理</span><span class="fa arrow"></span>
|
||||
</a>
|
||||
<ul class="nav nav-second-level">
|
||||
<li class="group"><a href="{% url 'users:user-list' %}">用户列表</a></li>
|
||||
<li class="user"><a href="{% url 'users:usergroup-list' %}">用户组列表</a></li>
|
||||
<ul class="nav nav-second-level active">
|
||||
<li class="user"><a href="{% url 'users:user-list' %}">用户列表</a></li>
|
||||
<li class="usergroup"><a href="{% url 'users:usergroup-list' %}">用户组列表</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li id="">
|
||||
|
@ -1,40 +1,38 @@
|
||||
{% load common_tags %}
|
||||
{% if is_paginated %}
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="dataTables_info" id="editable_info" role="status" aria-live="polite">
|
||||
Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ paginator.count }} entries
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="dataTables_paginate paging_simple_numbers" id="editable_paginate">
|
||||
<ul class="pagination" style="margin-top: 0; float: right">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="paginate_button previous" aria-controls="editable" tabindex="0" id="previous">
|
||||
<a data-page="next" href="?page={{ page_obj.previous_page_number}}">‹</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_paginated %}
|
||||
<div class="col-sm-4">
|
||||
<div class="dataTables_info text-center" id="editable_info" role="status" aria-live="polite">
|
||||
Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ paginator.count }} entries
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="dataTables_paginate paging_simple_numbers" id="editable_paginate">
|
||||
<ul class="pagination" style="margin-top: 0; float: right">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="paginate_button previous" aria-controls="editable" tabindex="0" id="previous">
|
||||
<a data-page="next" href="?page={{ page_obj.previous_page_number}}">‹</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% for page in paginator.num_pages|pagination_range:page_obj.number %}
|
||||
{% if page == page_obj.number %}
|
||||
<li class="paginate_button active" aria-controls="editable" tabindex="0">
|
||||
{% else %}
|
||||
<li class="paginate_button" aria-controls="editable" tabindex="0">
|
||||
{% endif %}
|
||||
<a class="page" href="?page={{ page }}" title="第{{ page }}页">{{ page }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% for page in paginator.num_pages|pagination_range:page_obj.number %}
|
||||
{% if page == page_obj.number %}
|
||||
<li class="paginate_button active" aria-controls="editable" tabindex="0">
|
||||
{% else %}
|
||||
<li class="paginate_button" aria-controls="editable" tabindex="0">
|
||||
{% endif %}
|
||||
<a class="page" href="?page={{ page }}" title="第{{ page }}页">{{ page }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<li class="paginate_button next" aria-controls="editable" tabindex="0" id="next">
|
||||
<a data-page="next" href="?page={{ page_obj.next_page_number }}">›</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if page_obj.has_next %}
|
||||
<li class="paginate_button next" aria-controls="editable" tabindex="0" id="next">
|
||||
<a data-page="next" href="?page={{ page_obj.next_page_number }}">›</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<script>
|
||||
function sleep(n) { //n表示的毫秒数
|
||||
var start = new Date().getTime();
|
||||
|
@ -20,6 +20,7 @@
|
||||
{% include '_left_side_bar.html' %}
|
||||
<div id="page-wrapper" class="gray-bg">
|
||||
{% include '_header_bar.html' %}
|
||||
{% include '_message.html' %}
|
||||
{% block content %}{% endblock %}
|
||||
{% include '_footer.html' %}
|
||||
</div>
|
||||
|
@ -21,7 +21,7 @@ class UserAddForm(ModelForm):
|
||||
}
|
||||
|
||||
widgets = {
|
||||
'groups': forms.SelectMultiple(attrs={'class': 'chosen-select', 'data-placeholder': '请选择用户组'}),
|
||||
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': '请选择用户组'}),
|
||||
}
|
||||
|
||||
|
||||
@ -29,19 +29,18 @@ class UserUpdateForm(ModelForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = [
|
||||
'name', 'email', 'groups', 'wechat', 'avatar',
|
||||
'name', 'email', 'groups', 'wechat',
|
||||
'phone', 'enable_otp', 'role', 'date_expired', 'comment',
|
||||
]
|
||||
|
||||
help_texts = {
|
||||
'username': '* required',
|
||||
'name': '* required',
|
||||
'email': '* required',
|
||||
'groups': '* required'
|
||||
}
|
||||
|
||||
widgets = {
|
||||
'groups': forms.SelectMultiple(attrs={'class': 'chosen-select', 'data-placeholder': '请选择用户组'}),
|
||||
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': '请选择用户组'}),
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,6 +7,7 @@ from django.contrib.auth.hashers import make_password
|
||||
from django.utils import timezone
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser, Permission
|
||||
from django.db import OperationalError
|
||||
|
||||
|
||||
class Role(models.Model):
|
||||
@ -23,6 +24,12 @@ class Role(models.Model):
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
def delete(self, using=None, keep_parents=False):
|
||||
if self.user_set.all().count() > 0:
|
||||
raise OperationalError('Role %s has some member, should not be delete.' % self.name)
|
||||
else:
|
||||
return super(Role, self).delete(using=using, keep_parents=keep_parents)
|
||||
|
||||
class Meta:
|
||||
db_table = 'role'
|
||||
|
||||
@ -56,7 +63,7 @@ class UserGroup(models.Model):
|
||||
|
||||
@classmethod
|
||||
def initial(cls):
|
||||
group_or_create = cls.objects.get_or_create(name='All', comment='Default user group for all user',
|
||||
group_or_create = cls.objects.get_or_create(name='Default', comment='Default user group for all user',
|
||||
created_by='System')
|
||||
return group_or_create[0]
|
||||
|
||||
@ -93,7 +100,7 @@ class User(AbstractUser):
|
||||
phone = models.CharField(max_length=20, blank=True, verbose_name='手机号')
|
||||
enable_otp = models.BooleanField(default=False, verbose_name='启用二次验证')
|
||||
secret_key_otp = models.CharField(max_length=16, blank=True)
|
||||
role = models.ForeignKey(Role, on_delete=models.PROTECT, verbose_name='角色')
|
||||
role = models.ForeignKey(Role, on_delete=models.SET('None'), verbose_name='角色')
|
||||
private_key = models.CharField(max_length=5000, blank=True, verbose_name='ssh私钥') # ssh key max length 4096 bit
|
||||
public_key = models.CharField(max_length=1000, blank=True, verbose_name='公钥')
|
||||
comment = models.TextField(max_length=200, blank=True, verbose_name='描述')
|
||||
@ -123,11 +130,13 @@ class User(AbstractUser):
|
||||
# If user not set name, it's default equal username
|
||||
if not self.name:
|
||||
self.name = self.username
|
||||
super(User, self).save(args, **kwargs)
|
||||
|
||||
super(User, self).save(*args, **kwargs)
|
||||
# Set user default group 'All'
|
||||
# Todo: It's have bug
|
||||
group = UserGroup.initial()
|
||||
self.groups.add(group)
|
||||
if group not in self.groups.all():
|
||||
self.groups.add(group)
|
||||
# super(User, self).save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
db_table = 'user'
|
||||
|
@ -2,9 +2,9 @@
|
||||
{% load static %}
|
||||
{% load bootstrap %}
|
||||
{% block custom_head_css_js %}
|
||||
<link href="{% static "css/plugins/chosen/chosen.css" %}" rel="stylesheet">
|
||||
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
|
||||
<link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/plugins/chosen/chosen.jquery.min.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
@ -41,13 +41,14 @@
|
||||
<div class="hr-line-dashed"></div>
|
||||
<h3>角色安全</h3>
|
||||
{{ form.role|bootstrap_horizontal }}
|
||||
<div class="form-group" id="date_5">
|
||||
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5">
|
||||
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group date">
|
||||
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
||||
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'m/d/Y' }}">
|
||||
</div>
|
||||
<span class="help-block ">{{ form.date_expired.errors }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@ -79,16 +80,7 @@
|
||||
<script src="{% static 'js/plugins/datapicker/bootstrap-datepicker.js' %}"></script>
|
||||
<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]);
|
||||
}
|
||||
$('.select2').select2();
|
||||
|
||||
$('.input-group.date').datepicker({
|
||||
todayBtn: "linked",
|
||||
|
@ -4,8 +4,8 @@
|
||||
{% load static %}
|
||||
|
||||
{% 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>
|
||||
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
@ -192,7 +192,7 @@
|
||||
<form>
|
||||
<tr>
|
||||
<td colspan="2" class="no-borders">
|
||||
<select data-placeholder="选择用户组" class="chosen-select" style="width: 100%" multiple="" tabindex="4">
|
||||
<select data-placeholder="选择用户组" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||
{% for group in groups %}
|
||||
<option value="{{ group.id }}">{{ group.name }}</option>
|
||||
{% endfor %}
|
||||
@ -208,7 +208,7 @@
|
||||
|
||||
{% for group in user.groups.all %}
|
||||
<tr>
|
||||
<td width="40%"><b style="font-size: medium">{{ group.name }}</b></td>
|
||||
<td ><b>{{ group.name }}</b></td>
|
||||
<td>
|
||||
<button class="btn btn-danger btn-sm" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
|
||||
</td>
|
||||
@ -230,16 +230,7 @@
|
||||
{% 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]);
|
||||
}
|
||||
$('.select2').select2();
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
92
apps/users/templates/users/user_list.bak.html
Normal file
92
apps/users/templates/users/user_list.bak.html
Normal file
@ -0,0 +1,92 @@
|
||||
{% extends '_list_base.html' %}
|
||||
{% load common_tags %}
|
||||
<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:user-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="" 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:user-list' %}?sort=name">姓名</a></th>
|
||||
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=username">用户名</a></th>
|
||||
<th class="text-center">角色</th>
|
||||
<th class="text-center">用户组</th>
|
||||
<th class="text-center">资产数量</th>
|
||||
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=date_expired">有效</a></th>
|
||||
<th class="text-center"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in user_list %}
|
||||
<tr class="gradeX">
|
||||
<td class="text-center">
|
||||
<input type="checkbox" name="checked" value="{{ user.id }}">
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'users:user-detail' pk=user.id %}">
|
||||
{{ user.name }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-center">{{ user.username }}</td>
|
||||
<td class="text-center">{{ user.role.name }}</td>
|
||||
<td class="text-center" title="{% for user_group in user.group.all %} {{ user_group.name }} {% endfor %}"> {{ user.groups.all|join_queryset_attr:"name" }} </td>
|
||||
<th class="text-center">{{ user.name }}</th>
|
||||
<td class="text-center">
|
||||
{% if user.is_expired %}
|
||||
<i class="fa fa-times text-danger"></i>
|
||||
{% else %}
|
||||
<i class="fa fa-check text-navy"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'users:user-edit' pk=user.id %}" class="btn btn-xs btn-info">编辑</a>
|
||||
<a href="{% url 'users:user-delete' pk=user.id %}" class="btn btn-xs btn-danger del {% if user.username == 'admin' %} disabled {% endif %}">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
</div>
|
||||
{% include '_pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -1,91 +1,71 @@
|
||||
{% extends 'base.html' %}
|
||||
{% extends '_list_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:user-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="" 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:user-list' %}?sort=name">姓名</a></th>
|
||||
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=username">用户名</a></th>
|
||||
<th class="text-center">角色</th>
|
||||
<th class="text-center">用户组</th>
|
||||
<th class="text-center">资产数量</th>
|
||||
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=date_expired">有效</a></th>
|
||||
<th class="text-center"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in user_list %}
|
||||
<tr class="gradeX">
|
||||
<td class="text-center">
|
||||
<input type="checkbox" name="checked" value="{{ user.id }}">
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'users:user-detail' pk=user.id %}">
|
||||
{{ user.name }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-center">{{ user.username }}</td>
|
||||
<td class="text-center">{{ user.role.name }}</td>
|
||||
<td class="text-center" title="{% for user_group in user.group.all %} {{ user_group.name }} {% endfor %}"> {{ user.groups.all|join_queryset_attr:"name" }} </td>
|
||||
<th class="text-center">{{ user.name }}</th>
|
||||
<td class="text-center">
|
||||
{% if user.is_expired %}
|
||||
<i class="fa fa-times text-danger"></i>
|
||||
{% else %}
|
||||
<i class="fa fa-check text-navy"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'users:user-edit' pk=user.id %}" class="btn btn-xs btn-info">编辑</a>
|
||||
<a href="{% url 'users:user-delete' pk=user.id %}" class="btn btn-xs btn-danger del {% if user.username == 'admin' %} disabled {% endif %}">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% include '_pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% block content_left_head %}
|
||||
<a href="{% url 'users:user-add' %}" class="btn btn-sm btn-primary "> 添加用户 </a>
|
||||
{# <a id="del_btn" class="btn btn-sm btn-danger "> 删除所选 </a>#}
|
||||
{% endblock %}
|
||||
|
||||
{% block table_head %}
|
||||
<th class="text-center">
|
||||
<input type="checkbox" id="check_all" onclick="checkAll('check_all', 'checked')">
|
||||
</th>
|
||||
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=name">姓名</a></th>
|
||||
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=username">用户名</a></th>
|
||||
<th class="text-center">角色</th>
|
||||
<th class="text-center">用户组</th>
|
||||
<th class="text-center">资产数量</th>
|
||||
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=date_expired">有效</a></th>
|
||||
<th class="text-center"></th>
|
||||
{% endblock %}
|
||||
|
||||
{% block table_body %}
|
||||
{% for user in user_list %}
|
||||
<tr class="gradeX">
|
||||
<td class="text-center">
|
||||
<input type="checkbox" name="checked" value="{{ user.id }}">
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'users:user-detail' pk=user.id %}">
|
||||
{{ user.name }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-center">{{ user.username }}</td>
|
||||
<td class="text-center">{{ user.role.name }}</td>
|
||||
<td class="text-center" title="{% for user_group in user.group.all %} {{ user_group.name }} {% endfor %}"> {{ user.groups.all|join_queryset_attr:"name" }} </td>
|
||||
<th class="text-center">{{ user.name }}</th>
|
||||
<td class="text-center">
|
||||
{% if user.is_expired %}
|
||||
<i class="fa fa-times text-danger"></i>
|
||||
{% else %}
|
||||
<i class="fa fa-check text-navy"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'users:user-edit' pk=user.id %}" class="btn btn-xs btn-info">编辑</a>
|
||||
<a href="{% url 'users:user-delete' pk=user.id %}" class="btn btn-xs btn-danger del {% if user.username == 'admin' %} disabled {% endif %}">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content_bottom_left %}
|
||||
<form id="" method="get" action="" class=" mail-search">
|
||||
<div class="input-group">
|
||||
<select class="form-control m-b" style="width: auto">
|
||||
<option>批量删除</option>
|
||||
<option>批量更新</option>
|
||||
<option>批量禁用</option>
|
||||
<option>批量导出</option>
|
||||
</select>
|
||||
|
||||
<div class="input-group-btn pull-left" style="padding-left: 5px;">
|
||||
<button id='search_btn' type="submit" style="height: 32px;" class="btn btn-sm btn-primary">
|
||||
确认
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
{% 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>
|
||||
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
@ -32,8 +32,8 @@
|
||||
|
||||
<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">
|
||||
<div class="col-sm-9">
|
||||
<select name="users" id="users" data-placeholder="选择用户" class="select2 form-control m-b" multiple tabindex="2">
|
||||
{% for user in users %}
|
||||
<option value="{{ user.id }}">{{ user.name }}</option>
|
||||
{% endfor %}
|
||||
@ -59,16 +59,7 @@
|
||||
{% 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]);
|
||||
}
|
||||
$('.select2').select2();
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
@ -1,81 +1,58 @@
|
||||
{% extends 'base.html' %}
|
||||
{% extends '_list_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>
|
||||
{% block content_left_head %}
|
||||
<a href="{% url 'users:usergroup-add' %}" class="btn btn-sm btn-primary "> 添加用户组 </a>
|
||||
{% endblock %}
|
||||
|
||||
<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="" 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>
|
||||
{% block table_head %}
|
||||
<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>
|
||||
{% endblock %}
|
||||
|
||||
<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 %}" class="btn btn-xs btn-info">编辑</a>
|
||||
<a href="{% url 'users:usergroup-delete' pk=usergroup.id %}" class="btn btn-xs btn-danger del">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% include '_pagination.html' %}
|
||||
</div>
|
||||
{% block table_body %}
|
||||
{% 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 %}" class="btn btn-xs btn-info">编辑</a>
|
||||
<a href="{% url 'users:usergroup-delete' pk=usergroup.id %}" class="btn btn-xs btn-danger del">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content_bottom_left %}
|
||||
<form id="" method="get" action="" class=" mail-search">
|
||||
<div class="input-group">
|
||||
<select class="form-control m-b" style="width: auto">
|
||||
<option>批量删除</option>
|
||||
<option>批量更新</option>
|
||||
<option>批量禁用</option>
|
||||
<option>批量导出</option>
|
||||
</select>
|
||||
|
||||
<div class="input-group-btn pull-left" style="padding-left: 5px;">
|
||||
<button id='search_btn' type="submit" style="height: 32px;" class="btn btn-sm btn-primary">
|
||||
确认
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -1,95 +0,0 @@
|
||||
# ~*~ coding: utf-8 ~*~
|
||||
|
||||
from random import choice
|
||||
import forgery_py
|
||||
|
||||
from django.utils import timezone
|
||||
from django.test import TestCase, Client, TransactionTestCase
|
||||
from django.test.utils import setup_test_environment
|
||||
from django.db import IntegrityError, transaction
|
||||
from .models import User, UserGroup, Role, init_all_models
|
||||
|
||||
|
||||
setup_test_environment()
|
||||
client = Client()
|
||||
|
||||
|
||||
def create_usergroup(name):
|
||||
pass
|
||||
|
||||
|
||||
def get_random_usergroup():
|
||||
pass
|
||||
|
||||
|
||||
def create_user(username, name, email, groups):
|
||||
pass
|
||||
|
||||
|
||||
def gen_username():
|
||||
return forgery_py.internet.user_name(True)
|
||||
|
||||
|
||||
def gen_email():
|
||||
return forgery_py.internet.email_address()
|
||||
|
||||
|
||||
def gen_name():
|
||||
return forgery_py.name.full_name()
|
||||
|
||||
|
||||
class UserModelTest(TransactionTestCase):
|
||||
def setUp(self):
|
||||
init_all_models()
|
||||
|
||||
def test_user_duplicate(self):
|
||||
# 创建一个基准测试用户
|
||||
role = choice(Role.objects.all())
|
||||
user = User(name='test', username='test', email='test@email.org', role=role)
|
||||
user.save()
|
||||
|
||||
# 创建一个姓名一致的用户, 应该创建成功
|
||||
user1 = User(name='test', username=gen_username(), password_raw=gen_username(),
|
||||
email=gen_email(), role=role)
|
||||
try:
|
||||
user1.save()
|
||||
user1.delete()
|
||||
except IntegrityError:
|
||||
self.assertTrue(0, 'Duplicate <name> not allowed.')
|
||||
|
||||
# 创建一个用户名一致的用户, 应该创建不成功
|
||||
user2 = User(username='test', email=gen_email(), role=role)
|
||||
|
||||
try:
|
||||
user2.save()
|
||||
self.assertTrue(0, 'Duplicate <username> allowed.')
|
||||
except IntegrityError:
|
||||
pass
|
||||
|
||||
# 创建一个Email一致的用户,应该创建不成功
|
||||
user3 = User(username=gen_username(), email='test@email.org', role=role)
|
||||
try:
|
||||
user3.save()
|
||||
self.assertTrue(0, 'Duplicate <email> allowed.')
|
||||
except IntegrityError:
|
||||
pass
|
||||
|
||||
def test_user_was_expired(self):
|
||||
role = choice(Role.objects.all())
|
||||
date = timezone.now() - timezone.timedelta(days=1)
|
||||
user = User(name=gen_name(), username=gen_username(),
|
||||
email=gen_email(), role=role, date_expired=date)
|
||||
|
||||
self.assertTrue(user.is_expired())
|
||||
|
||||
def test_user_with_default_group(self):
|
||||
role = choice(Role.objects.all())
|
||||
user = User(username=gen_username(), email=gen_email(), role=role)
|
||||
user.save()
|
||||
|
||||
self.assertEqual(user.groups.count(), 1)
|
||||
self.assertEqual(user.groups.first().name, 'All')
|
||||
|
||||
|
||||
class UserListViewTests(TestCase):
|
||||
pass
|
0
apps/users/tests/__init__.py
Normal file
0
apps/users/tests/__init__.py
Normal file
24
apps/users/tests/base.py
Normal file
24
apps/users/tests/base.py
Normal file
@ -0,0 +1,24 @@
|
||||
# ~*~ coding: utf-8 ~*~
|
||||
|
||||
|
||||
from random import choice
|
||||
import forgery_py
|
||||
|
||||
from users.models import User, UserGroup, Role, init_all_models
|
||||
|
||||
|
||||
def gen_username():
|
||||
return forgery_py.internet.user_name(True)
|
||||
|
||||
|
||||
def gen_email():
|
||||
return forgery_py.internet.email_address()
|
||||
|
||||
|
||||
def gen_name():
|
||||
return forgery_py.name.full_name()
|
||||
|
||||
|
||||
def get_role():
|
||||
role = choice(Role.objects.all())
|
||||
return role
|
109
apps/users/tests/test_models.py
Normal file
109
apps/users/tests/test_models.py
Normal file
@ -0,0 +1,109 @@
|
||||
# ~*~ coding: utf-8 ~*~
|
||||
|
||||
|
||||
from django.utils import timezone
|
||||
from django.shortcuts import reverse
|
||||
from django.test import TestCase, Client, TransactionTestCase
|
||||
from django.db import IntegrityError
|
||||
from users.models import User, UserGroup, Role, init_all_models
|
||||
from django.contrib.auth.models import Permission
|
||||
|
||||
from .base import gen_name, gen_username, gen_email, get_role
|
||||
|
||||
|
||||
class UserModelTest(TransactionTestCase):
|
||||
def setUp(self):
|
||||
init_all_models()
|
||||
|
||||
# 创建一个用户用于测试
|
||||
role = get_role()
|
||||
user = User(name='test', username='test', email='test@email.org', role=role)
|
||||
user.save()
|
||||
|
||||
def test_initial(self):
|
||||
self.assertEqual(User.objects.all().count(), 2)
|
||||
self.assertEqual(Role.objects.all().count(), 3)
|
||||
self.assertEqual(UserGroup.objects.all().count(), 1)
|
||||
|
||||
@property
|
||||
def role(self):
|
||||
return get_role()
|
||||
|
||||
# 创建一个姓名一致的用户, 应该创建成功
|
||||
def test_user_name_duplicate(self):
|
||||
user1 = User(name='test', username=gen_username(), password_raw=gen_username(),
|
||||
email=gen_email(), role=self.role)
|
||||
try:
|
||||
user1.save()
|
||||
user1.delete()
|
||||
except IntegrityError:
|
||||
self.assertTrue(0, 'Duplicate <name> not allowed.')
|
||||
|
||||
# 创建一个用户名一致的用户, 应该创建不成功
|
||||
def test_user_username_duplicate(self):
|
||||
user2 = User(username='test', email=gen_email(), role=self.role)
|
||||
|
||||
with self.assertRaises(IntegrityError):
|
||||
user2.save()
|
||||
|
||||
# 创建一个Email一致的用户,应该创建不成功
|
||||
def test_user_email_duplicate(self):
|
||||
user3 = User(username=gen_username(), email='test@email.org', role=self.role)
|
||||
|
||||
with self.assertRaises(IntegrityError):
|
||||
user3.save()
|
||||
|
||||
# 用户过期测试
|
||||
def test_user_was_expired(self):
|
||||
date = timezone.now() - timezone.timedelta(days=1)
|
||||
user = User(name=gen_name(), username=gen_username(),
|
||||
email=gen_email(), role=self.role, date_expired=date)
|
||||
|
||||
self.assertTrue(user.is_expired())
|
||||
|
||||
# 测试用户默认会输入All用户组
|
||||
def test_user_with_default_group(self):
|
||||
role = get_role()
|
||||
user = User(username=gen_username(), email=gen_email(), role=role)
|
||||
user.save()
|
||||
|
||||
self.assertEqual(user.groups.count(), 1)
|
||||
self.assertEqual(user.groups.first().name, 'Default')
|
||||
|
||||
def test_user_password_authenticated(self):
|
||||
password = gen_username() * 3
|
||||
user = User(username=gen_username(), password_raw=password, role=self.role)
|
||||
user.save()
|
||||
self.assertTrue(user.check_password(password))
|
||||
self.assertFalse(user.check_password(password*2))
|
||||
|
||||
def tearDown(self):
|
||||
User.objects.all().delete()
|
||||
UserGroup.objects.all().delete()
|
||||
Role.objects.all().delete()
|
||||
|
||||
|
||||
class RoleModelTestCase(TransactionTestCase):
|
||||
def setUp(self):
|
||||
Role.objects.all().delete()
|
||||
Role.initial()
|
||||
|
||||
def test_role_initial(self):
|
||||
self.assertEqual(Role.objects.all().count(), 3)
|
||||
|
||||
def test_create_new_role(self):
|
||||
role = Role(name=gen_name(), comment=gen_name()*3)
|
||||
role.save()
|
||||
role.permissions = Permission.objects.all()
|
||||
role.save()
|
||||
|
||||
self.assertEqual(Role.objects.count(), 4)
|
||||
role = Role.objects.last()
|
||||
self.assertEqual(role.permissions.all().count(), Permission.objects.all().count())
|
||||
|
||||
|
||||
class UserGroupModelTestCase(TransactionTestCase):
|
||||
pass
|
||||
|
||||
|
||||
|
58
apps/users/tests/test_views.py
Normal file
58
apps/users/tests/test_views.py
Normal file
@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from users.models import User, UserGroup, Role, init_all_models
|
||||
from django.shortcuts import reverse
|
||||
from django.test import TestCase, Client, TransactionTestCase
|
||||
|
||||
from .base import gen_username, gen_name, gen_email, get_role
|
||||
|
||||
|
||||
class UserListViewTests(TransactionTestCase):
|
||||
def setUp(self):
|
||||
init_all_models()
|
||||
|
||||
def test_a_new_user_in_list(self):
|
||||
username = gen_username()
|
||||
user = User(username=username, email=gen_email(), role=get_role())
|
||||
user.save()
|
||||
response = self.client.get(reverse('users:user-list'))
|
||||
|
||||
self.assertContains(response, username)
|
||||
|
||||
def test_list_view_with_admin_user(self):
|
||||
response = self.client.get(reverse('users:user-list'))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, 'Admin')
|
||||
self.assertEqual(response.context['user_list'].count(), User.objects.all().count())
|
||||
|
||||
def test_pagination(self):
|
||||
User.generate_fake(count=20)
|
||||
response = self.client.get(reverse('users:user-list'))
|
||||
self.assertEqual(response.context['is_paginated'], True)
|
||||
|
||||
|
||||
class UserAddTests(TestCase):
|
||||
def setUp(self):
|
||||
init_all_models()
|
||||
|
||||
def test_add_a_new_user(self):
|
||||
username = gen_username()
|
||||
data = {
|
||||
'username': username,
|
||||
'comment': '',
|
||||
'name': gen_name(),
|
||||
'email': gen_email(),
|
||||
'groups': [UserGroup.objects.first().id, ],
|
||||
'role': get_role().id,
|
||||
'date_expired': '2086-08-06 19:12:22',
|
||||
}
|
||||
|
||||
response = self.client.post(reverse('users:user-add'), data)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response['location'], reverse('users:user-list'))
|
||||
|
||||
response = self.client.get(reverse('users:user-list'))
|
||||
self.assertContains(response, username)
|
||||
|
@ -1,11 +1,14 @@
|
||||
# ~*~ coding: utf-8 ~*~
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.shortcuts import get_object_or_404, reverse
|
||||
from django.urls import reverse_lazy
|
||||
from django.db.models import Q
|
||||
from django.views.generic.list import ListView
|
||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.conf import settings
|
||||
|
||||
from .models import User, UserGroup, Role
|
||||
@ -37,11 +40,12 @@ class UserListView(ListView):
|
||||
return context
|
||||
|
||||
|
||||
class UserAddView(CreateView):
|
||||
class UserAddView(SuccessMessageMixin, CreateView):
|
||||
model = User
|
||||
form_class = UserAddForm
|
||||
template_name = 'users/user_add.html'
|
||||
success_url = reverse_lazy('users:user-list')
|
||||
success_message = '添加用户 <a href="%s">%s</a> 成功 .'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserAddView, self).get_context_data(**kwargs)
|
||||
@ -54,6 +58,12 @@ class UserAddView(CreateView):
|
||||
user.save()
|
||||
return super(UserAddView, self).form_valid(form)
|
||||
|
||||
def get_success_message(self, cleaned_data):
|
||||
return self.success_message % (
|
||||
reverse_lazy('users:user-detail', kwargs={'pk': self.object.pk}),
|
||||
self.object.name,
|
||||
)
|
||||
|
||||
|
||||
class UserUpdateView(UpdateView):
|
||||
model = User
|
||||
@ -72,6 +82,10 @@ class UserUpdateView(UpdateView):
|
||||
user.set_password(password)
|
||||
return super(UserUpdateView, self).form_valid(form)
|
||||
|
||||
def form_invalid(self, form):
|
||||
print(form.errors)
|
||||
return super(UserUpdateView, self).form_invalid(form)
|
||||
|
||||
|
||||
class UserDeleteView(DeleteView):
|
||||
model = User
|
||||
@ -93,7 +107,7 @@ class UserDetailView(DetailView):
|
||||
|
||||
class UserGroupListView(ListView):
|
||||
model = UserGroup
|
||||
paginate_by = 20
|
||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||
context_object_name = 'usergroup_list'
|
||||
template_name = 'users/usergroup_list.html'
|
||||
ordering = '-date_added'
|
||||
|
Loading…
Reference in New Issue
Block a user