mirror of https://github.com/jumpserver/jumpserver
Merge branch 'master' of code.simcu.com:jumpserver/jumpserver
commit
7cebc4efe8
|
@ -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 */
|
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
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="">
|
||||
|
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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,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
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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