Commit 0d4ca971 authored by ibuler's avatar ibuler

Merge branch 'master' into connect

parents ab18fe46 7ab47916
......@@ -11,9 +11,8 @@ build
_mailinglist
.tox
.cache/
migrations/
.idea/
db.sqlite3
config.py
*/migrations/*
migrations/
*.log
# ~*~ coding: utf-8 ~*~
from rest_framework import serializers
from rest_framework import viewsets, serializers
from rest_framework import viewsets, serializers,generics
from .models import AssetGroup, Asset, IDC, AssetExtend
......@@ -45,9 +44,11 @@ class AssetViewSet(viewsets.ModelViewSet):
serializer_class = AssetSerializer
class IDCViewSet(viewsets.ModelViewSet):
class IDCViewSet(viewsets.ReadOnlyModelViewSet):
"""
API endpoint that allows IDC to be viewed or edited.
"""
queryset = IDC.objects.all()
serializer_class = IDCSerializer
\ No newline at end of file
serializer_class = IDCSerializer
......@@ -91,7 +91,7 @@ class AssetGroupForm(forms.ModelForm):
class Meta:
model = AssetGroup
fields = [
"name", "comment","system_users"
"name", "comment","system_users",
]
widgets = {
'name' : forms.TextInput(attrs={}),
......
......@@ -47,7 +47,6 @@ class IDC(models.Model):
contact=forgery_py.name.full_name(),
phone=forgery_py.address.phone(),
address=forgery_py.address.city() + forgery_py.address.street_address(),
network="192.168.1.10/24\n192.168.1.20",
operator=choice(['北京联通', '北京电信', 'BGP全网通']),
comment=forgery_py.lorem_ipsum.sentence(),
created_by='Fake')
......
......@@ -3,8 +3,10 @@
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<link href="{% static 'css/plugins/dataTables/datatables.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/dataTables/datatables.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
......@@ -37,18 +39,13 @@
{{ form.name|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<h3 class="widget-head-color-box">资产</h3>
<div class="form-group">
<label for="assets" class="col-sm-2 control-label">资产</label>
<div class="col-sm-9">
<select name="assets" id="assets" data-placeholder="选择资产" class="select2 form-control m-b" multiple tabindex="2">
{% for asset in assets %}
<option value="{{ asset.id }}">{{ asset.hostname }}</option>
{% endfor %}
</select>
</div>
</div>
<!--{{ form.name|bootstrap_horizontal }}-->
<h3 class="widget-head-color-box">资产数量</h3>
<div class="form-group">
<label class="col-sm-2 control-label">已选</label>
<div class="col-sm-9" id="asset_sed">
<input type="text" class=" form-control" id="add_asset" value="{{ assets_count }}">
</div>
</div>
<div class="hr-line-dashed"></div>
<h3 class="widget-head-color-box">资产用户</h3>
{{ form.system_users|bootstrap_horizontal }}
......@@ -56,6 +53,7 @@
<div class="col-sm-4 col-sm-offset-5">
<button class="btn btn-white" type="reset"> 重置 </button>
<button class="btn btn-primary" type="submit"> 提交 </button>
<div id='box2'> </div>
</div>
</div>
</form>
......@@ -68,13 +66,55 @@
</div>
</div>
</div>
<!-- 模态框(Modal) -->
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content" id="box">
<!--此部分为主体内容,将远程加载进来-->
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
$('.select2').select2();
$('.select2-system-user').select2();
})
</script>
<script type="text/javascript">
$(document).ready(function () {
$('.select2').select2();
$('.select2-system-user').select2();
})
$('#add_asset').on('click',function(){
$('#modal').modal('show');
});
$('#modal').modal({
show: false,
backdrop: 'static',
keyboard: 'false',
remote:"{% url 'assets:asset-modal-list' %}?group_id={{ group_id }}",
});
$('#modal').on('show.bs.modal',function(){
//alert('当调用show方法时,立即触发;')
});
$('#modal').on('shown.bs.modal',function(){
//alert('当弹窗完全加载完后,再触发;')
});
$('#modal').on('hide.bs.modal',function(){
//alert('当关闭时,立即触发;')
});
$('#modal').on('hidden.bs.modal',function(){
//alert('当关完全关闭后,再触发;')
});
$('#modal').on('loaded.bs.modal',function(){
//alert('当远程数据加载完毕后,再触发;')
});
</script>
{% endblock %}
\ No newline at end of file
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel">分配/回收资产</h4>
</div>
<div class="modal-body" style="padding-bottom: 0px;">
<table aria-describedby="editable_info" role="grid" class="table table-striped table-bordered table-hover dataTable" id="editable">
<thead>
<tr>
<th class="text-center" style="background-color:white">
<input type="checkbox" id="check_all" onclick="checkAll('check_all', 'checked')">
</th>
<th>资产名称</th>
<th>IP</th>
<th>硬件类型</th>
<th>资产组</th>
<th>部门</th>
</tr>
</thead>
<tbody>
{% for asset in asset_modal_list %}
<tr>
{% if asset.id in group_assets %}
<td class="text-center" ><input type="checkbox" name="checked" value="{{ asset.id }}" checked="checked"></td>
{% else %}
<td class="text-center" ><input type="checkbox" name="checked" value="{{ asset.id }}"></td>
{% endif %}
<td>{{ asset.hostname }}</td>
<td>{{ asset.ip }}</td>
<td>虚拟机</td>
<td>网络设备</td>
<td>微信事业部</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" id="close-btn">取消</button>
<button type="button" class="btn btn-primary" id="save-btn">保存</button>
</div>
<script type="text/javascript">
$('#close-btn').on('click',function(){
//alert('点击关闭按钮时触发;')
$('#modal').modal('hide');
});
$('#save-btn').on('click',function(){
// alert('点击保存按钮时触发2;')
var box = $("input[name='checked']:checked");
var size=box.length;
document.getElementById('add_asset').value = size;
var ids=[];
$("#asset_sed").find("input[name='assets']").remove();
for(var i=0;i<box.length;i++){
var value = $(box[i]).val();
$("#asset_sed").append("<input type='hidden' name='assets' value='"+value+"'>");
}
$('#modal').modal('hide');
});
$(document).ready(function(){
$('#editable').DataTable({
"aaSorting": [[1, "asc"]], //给列表排序 ,第一个参数表示数组 (由0开始)。第二个参数为 desc或是asc
"aoColumnDefs": [ { "bSortable": false, "aTargets": [ 0 ] }], // 0列不参加排序
"aLengthMenu": [[10, 25, 50, -1], ["10", "25", "50", "all"]], //设置每页显示记录的下拉菜单
"oLanguage": {
"sLengthMenu": "每页显示 _MENU_ 条记录",
"sZeroRecords": "对不起,查询不到任何相关数据",
"sInfo": "当前显示 _START_ 到 _END_ 条,共 _TOTAL_ 条记录",
"sInfoEmtpy": "找不到相关数据",
"sInfoFiltered": "数据表中共为 _MAX_ 条记录)",
"sProcessing": "正在加载中...",
"sSearch": "搜索",
"sUrl": "", //多语言配置文件,可将oLanguage的设置放在一个txt文件中,例:Javascript/datatable/dtCH.txt
"oPaginate": {
"sFirst": "第一页",
"sPrevious": " 上一页 ",
"sNext": " 下一页 ",
"sLast": " 最后一页 "
}
} //多语言配置
});
/* Init DataTables */
var oTable = $('#editable').DataTable();
/* Apply the jEditable handlers to the table */
oTable.$('td').editable( '../example_ajax.php', {
"callback": function( sValue, y ) {
var aPos = oTable.fnGetPosition( this );
oTable.fnUpdate( sValue, aPos[0], aPos[1] );
},
"submitdata": function ( value, settings ) {
return {
"row_id": this.parentNode.getAttribute('id'),
"column": oTable.fnGetPosition( this )[2]
};
},
"width": "90%",
"height": "100%",
});
});
function fnClickAddRow() {
$('#editable').dataTable().fnAddData( [
"Custom row",
"New row",
"New row",
"New row",
"New row" ] );
}
</script>
......@@ -9,13 +9,10 @@
<th class="text-center">
<input type="checkbox" id="check_all" onclick="checkAll('check_all', 'checked')">
</th>
{# <th class="text-center">{% trans 'ID' %}</th>#}
<th class="text-center"><a href="{% url 'assets:idc-list' %}?sort=name">{% trans 'Name' %}</a></th>
<th class="text-center">{% trans 'Asset num' %}</th>
{# <th class="text-center">{% trans 'Bandwidth' %}</th>#}
<th class="text-center">{% trans 'Contact' %}</th>
<th class="text-center">{% trans 'Phone' %}</th>
{# <th class="text-center">{% trans 'Address' %}</th>#}
<th class="text-center">{% trans 'operation' %}</th>
{% endblock %}
......
# coding:utf-8
from django.conf.urls import url, include
import views
import api
# from .api import (
# AssetGroupViewSet, AssetViewSet, IDCViewSet
# )
......@@ -19,6 +20,7 @@ urlpatterns = [
url(r'^asset/(?P<pk>[0-9]+)$', views.AssetDetailView.as_view(), name='asset-detail'),
url(r'^asset/(?P<pk>[0-9]+)/update', views.AssetUpdateView.as_view(), name='asset-update'),
url(r'^asset/(?P<pk>[0-9]+)/delete$', views.AssetDeleteView.as_view(), name='asset-delete'),
url(r'^asset-modal$', views.AssetModalListView.as_view(), name='asset-modal-list'),
# Resource asset group url
url(r'^asset-group$', views.AssetGroupListView.as_view(), name='asset-group-list'),
......@@ -50,5 +52,13 @@ urlpatterns = [
url(r'^system-user/(?P<pk>[0-9]+)/asset$', views.SystemUserAssetView.as_view(), name='system-user-asset'),
# url(r'^system-user/(?P<pk>[0-9]+)/asset-group$', views.SystemUserAssetGroupView.as_view(),
# name='system-user-asset-group'),
# url(r'^api/v1.0/', include(router.urls)),
]
urlpatterns += [
#json
url(r'^v1/assets/$', api.AssetViewSet.as_view({'get':'list'}), name='assets-list-api'),
url(r'^v1/idc/$', api.IDCViewSet.as_view({'get':'list'}), name='idc-list-json'),
]
# ~*~ coding: utf-8 ~*~
#
from rest_framework import serializers
from models import Asset
......@@ -100,6 +100,22 @@ class AssetDetailView(DetailView):
kwargs.update(context)
return super(AssetDetailView, self).get_context_data(**kwargs)
class AssetModalListView(AdminUserRequiredMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
model = Asset
context_object_name = 'asset_modal_list'
template_name = 'assets/asset_modal_list.html'
def get_context_data(self, **kwargs):
group_id = self.request.GET.get('group_id')
if group_id:
group = AssetGroup.objects.get(id=group_id)
context = {
'group_assets':[x.id for x in group.assets.all()]
}
kwargs.update(context)
return super(AssetModalListView, self).get_context_data(**kwargs)
class AssetGroupCreateView(AdminUserRequiredMixin, CreateView):
model = AssetGroup
......@@ -113,8 +129,7 @@ class AssetGroupCreateView(AdminUserRequiredMixin, CreateView):
context = {
'app': _('Assets'),
'action': _('Create asset group'),
'assets': Asset.objects.all(),
# 'systemusers':SystemUser.objects.all(),
'assets_count': 0,
}
kwargs.update(context)
return super(AssetGroupCreateView, self).get_context_data(**kwargs)
......@@ -123,7 +138,8 @@ class AssetGroupCreateView(AdminUserRequiredMixin, CreateView):
def form_valid(self, form):
asset_group = form.save()
assets_id_list = self.request.POST.getlist('assets', [])
assets = [get_object_or_404(Asset, id=asset_id) for asset_id in assets_id_list]
assets = [get_object_or_404(Asset, id=int(asset_id)) for asset_id in assets_id_list]
print assets
asset_group.created_by = self.request.user.username or 'Admin'
asset_group.assets.add(*tuple(assets))
asset_group.save()
......@@ -148,11 +164,9 @@ class AssetGroupListView(AdminUserRequiredMixin, ListView):
self.queryset = super(AssetGroupListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '')
self.sort = sort = self.request.GET.get('sort', '-date_created')
if keyword:
self.queryset = self.queryset.filter(Q(name__icontains=keyword) |
Q(comment__icontains=keyword))
if sort:
self.queryset = self.queryset.order_by(sort)
return self.queryset
......@@ -178,19 +192,23 @@ class AssetGroupDetailView(SingleObjectMixin, AdminUserRequiredMixin, ListView):
kwargs.update(context)
return super(AssetGroupDetailView, self).get_context_data(**kwargs)
class AssetGroupUpdateView(AdminUserRequiredMixin, UpdateView):
model = AssetGroup
form_class = AssetGroupForm
template_name = 'assets/asset_group_create.html'
success_url = reverse_lazy('assets:asset-group-list')
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=AssetGroup.objects.all())
return super(AssetGroupUpdateView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Create asset group'),
'assets': Asset.objects.all(),
# 'assets': Asset.objects.all(),
'assets_count': self.object.assets.all().count(),
'group_id':self.object.id,
}
kwargs.update(context)
return super(AssetGroupUpdateView, self).get_context_data(**kwargs)
......@@ -283,6 +301,7 @@ class IDCDeleteView(AdminUserRequiredMixin, DeleteView):
success_url = reverse_lazy('assets:idc-list')
class AdminUserListView(AdminUserRequiredMixin, ListView):
model = AdminUser
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
......@@ -527,3 +546,5 @@ class SystemUserAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
# kwargs.update(context)
# return super(SystemUserAssetGroupView, self).get_context_data(**kwargs)
This diff is collapsed.
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
This source diff could not be displayed because it is too large. You can view the blob instead.
File mode changed from 100755 to 100644
This diff is collapsed.
/**
* English - this is the default DataTables ships with
* @name English
* @anchor English
* @author <a href="http://www.sprymedia.co.uk/">Allan Jardine</a>
*/
{
"sEmptyTable": "No data available in table",
"sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
"sInfoEmpty": "Showing 0 to 0 of 0 entries",
"sInfoFiltered": "(filtered from _MAX_ total entries)",
"sInfoPostFix": "",
"sInfoThousands": ",",
"sLengthMenu": "Show _MENU_ entries",
"sLoadingRecords": "Loading...",
"sProcessing": "Processing...",
"sSearch": "Search:",
"sZeroRecords": "No matching records found",
"oPaginate": {
"sFirst": "First",
"sLast": "Last",
"sNext": "Next",
"sPrevious": "Previous"
},
"oAria": {
"sSortAscending": ": activate to sort column ascending",
"sSortDescending": ": activate to sort column descending"
}
}
\ No newline at end of file
{
"sProcessing": "处理中...",
"sLengthMenu": "显示 _MENU_ 项结果",
"sZeroRecords": "没有匹配结果",
"sInfo": "显示第 _START_ 至 _END_ 项结果,共 _TOTAL_ 项",
"sInfoEmpty": "显示第 0 至 0 项结果,共 0 项",
"sInfoFiltered": "(由 _MAX_ 项结果过滤)",
"sInfoPostFix": "",
"sSearch": "搜索:",
"sUrl": "",
"sEmptyTable": "表中数据为空",
"sLoadingRecords": "载入中...",
"sInfoThousands": ",",
"oPaginate": {
"sFirst": "首页",
"sPrevious": "上页",
"sNext": "下页",
"sLast": "末页"
},
"oAria": {
"sSortAscending": ": 以升序排列此列",
"sSortDescending": ": 以降序排列此列"
}
}
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
{% extends 'base.html' %}
{% load static %}
{% load common_tags %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/dataTables/dataTables.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/dataTables/dataTables.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
......@@ -19,10 +24,10 @@
</a>
</div>
</div>
<div class="ibox-content">
<div class="">
{% block content_left_head %} {% endblock %}
{% block table_search %}
<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 }}">
......@@ -33,8 +38,9 @@
</div>
</div>
</form>
{% endblock %}
</div>
{% block table_container %}
<table class="table table-striped table-bordered table-hover " id="editable" >
<thead>
<tr>
......@@ -45,17 +51,19 @@
{% block table_body %} {% endblock %}
</tbody>
</table>
{% endblock %}
<div class="row">
<div class="col-sm-4">
{# Update batch #}
{% block content_bottom_left %} {% endblock %}
</div>
{% block table_pagination %}
{% include '_pagination.html' %}
{% endblock %}
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
......@@ -4,9 +4,10 @@
import logging
from rest_framework import generics
from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView
from .serializers import UserSerializer, UserGroupSerializer, UserAttributeSerializer, UserGroupEditSerializer, \
GroupEditSerializer, UserPKUpdateSerializer
GroupEditSerializer, UserPKUpdateSerializer, UserBulkUpdateSerializer
from .models import User, UserGroup
......@@ -92,3 +93,21 @@ class UserUpdatePKApi(generics.UpdateAPIView):
class GroupDeleteApi(generics.DestroyAPIView):
queryset = UserGroup.objects.all()
serializer_class = GroupEditSerializer
class UserBulkUpdateApi(ListBulkCreateUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserBulkUpdateSerializer
def filter_queryset(self, queryset):
id_list = self.request.query_params.get('id__in')
if id_list:
import json
try:
ids = json.loads(id_list)
except Exception as e:
logger.error(str(e))
return queryset
if isinstance(ids, list):
queryset = queryset.filter(id__in=ids)
return queryset
......@@ -215,6 +215,11 @@ class User(AbstractUser):
user.save()
user.groups.add(UserGroup.initial())
def delete(self):
if self.is_superuser:
return
return super(User, self).delete()
@classmethod
def generate_fake(cls, count=100):
from random import seed, choice
......
......@@ -3,6 +3,7 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin
from .models import User, UserGroup
......@@ -67,3 +68,22 @@ class UserPKUpdateSerializer(serializers.ModelSerializer):
print e
raise serializers.ValidationError(_('Not a valid ssh public key'))
return value
class UserBulkUpdateSerializer(BulkSerializerMixin, serializers.ModelSerializer):
group_display = serializers.SerializerMethodField()
active_display = serializers.SerializerMethodField()
class Meta(object):
model = User
list_serializer_class = BulkListSerializer
fields = ['id', 'is_active', 'username', 'name', 'email', 'role', 'avatar',
'enable_otp', 'comment', 'groups', 'get_role_display',
'group_display', 'active_display']
def get_group_display(self, obj):
return " ".join([group.name for group in obj.groups.all()])
def get_active_display(self, obj):
# TODO: user ative state
return not (obj.is_expired and obj.is_active)
This diff is collapsed.
......@@ -36,6 +36,7 @@ urlpatterns = [
urlpatterns += [
url(r'^v1/users$', api.UserListAddApi.as_view(), name='user-list-api'),
url(r'^v1/users/update/$', api.UserBulkUpdateApi.as_view(), name='user-bulk-update-api'),
url(r'^v1/users/(?P<pk>[0-9]+)$',
api.UserDetailDeleteUpdateApi.as_view(), name='user-detail-api'),
url(r'^v1/users/(?P<pk>[0-9]+)/patch$',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment