提交 2d396787 编写于 作者: baltery's avatar baltery

[Feature] 完成资产授权和资产添加

上级 653c328e
......@@ -23,7 +23,7 @@ from django.shortcuts import get_object_or_404
from django.db.models import Q, Count
from django.utils.translation import ugettext_lazy as _
from common.mixins import CustomFilterMixin
from common.mixins import IDInFilterMixin
from common.utils import get_logger
from .hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser, \
get_user_granted_assets
......@@ -38,7 +38,7 @@ from .utils import LabelFilter
logger = get_logger(__file__)
class AssetViewSet(CustomFilterMixin, LabelFilter, BulkModelViewSet):
class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet):
"""
API endpoint that allows Asset to be viewed or edited.
"""
......@@ -56,6 +56,7 @@ class AssetViewSet(CustomFilterMixin, LabelFilter, BulkModelViewSet):
asset_group_id = self.request.query_params.get('asset_group_id')
admin_user_id = self.request.query_params.get('admin_user_id')
system_user_id = self.request.query_params.get('system_user_id')
node_id = self.request.query_params.get("node_id")
if cluster_id:
queryset = queryset.filter(cluster__id=cluster_id)
......@@ -70,6 +71,9 @@ class AssetViewSet(CustomFilterMixin, LabelFilter, BulkModelViewSet):
system_user = get_object_or_404(SystemUser, id=system_user_id)
clusters = system_user.get_clusters()
queryset = queryset.filter(cluster__in=clusters)
if node_id:
node = get_object_or_404(Node, id=node_id)
queryset = queryset.filter(nodes__key__startswith=node.key)
return queryset
......@@ -86,7 +90,7 @@ class UserAssetListView(generics.ListAPIView):
return queryset
class AssetGroupViewSet(CustomFilterMixin, BulkModelViewSet):
class AssetGroupViewSet(IDInFilterMixin, BulkModelViewSet):
"""
Asset group api set, for add,delete,update,list,retrieve resource
"""
......@@ -120,7 +124,7 @@ class GroupAddAssetsApi(generics.UpdateAPIView):
return Response({'error': serializer.errors}, status=400)
class ClusterViewSet(CustomFilterMixin, BulkModelViewSet):
class ClusterViewSet(IDInFilterMixin, BulkModelViewSet):
"""
Cluster api set, for add,delete,update,list,retrieve resource
"""
......@@ -161,7 +165,7 @@ class ClusterAddAssetsApi(generics.UpdateAPIView):
return Response({'error': serializer.errors}, status=400)
class AdminUserViewSet(CustomFilterMixin, BulkModelViewSet):
class AdminUserViewSet(IDInFilterMixin, BulkModelViewSet):
"""
Admin user api set, for add,delete,update,list,retrieve resource
"""
......@@ -197,7 +201,7 @@ class SystemUserViewSet(BulkModelViewSet):
permission_classes = (IsSuperUserOrAppUser,)
class AssetListUpdateApi(CustomFilterMixin, ListBulkCreateUpdateDestroyAPIView):
class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
"""
Asset bulk update api
"""
......@@ -318,8 +322,8 @@ class NodeViewSet(BulkModelViewSet):
serializer_class = serializers.NodeSerializer
def perform_create(self, serializer):
child_id = Node.root().get_next_child_id()
serializer.validated_data["id"] = child_id
child_key = Node.root().get_next_child_key()
serializer.validated_data["key"] = child_key
serializer.save()
......@@ -330,17 +334,20 @@ class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
instance = None
def post(self, request, *args, **kwargs):
if not request.data.get("name"):
request.data["name"] = _("New node {}").format(
Node.root().get_next_child_id().split(":")[-1]
if not request.data.get("value"):
request.data["value"] = _("New node {}").format(
Node.root().get_next_child_key().split(":")[-1]
)
return super().post(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
instance = self.get_object()
name = request.data.get("name")
node = instance.create_child(name=name)
return Response({"id": node.id, "name": node.name}, status=201)
value = request.data.get("value")
node = instance.create_child(value=value)
return Response(
{"id": node.id, "key": node.key, "value": node.value},
status=201,
)
def get(self, request, *args, **kwargs):
instance = self.get_object()
......@@ -348,5 +355,5 @@ class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
children = instance.get_all_children()
else:
children = instance.get_children()
response = [{"id": node.id, "name": node.name} for node in children]
response = [{"id": node.id, "key": node.key, "value": node.value} for node in children]
return Response(response, status=200)
......@@ -17,7 +17,14 @@ class Node(models.Model):
date_create = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.full_value
@property
def full_value(self):
if self == self.__class__.root():
return self.value
else:
return '{}/{}'.format( self.value, self.parent.full_value)
@property
def level(self):
......@@ -52,6 +59,21 @@ class Node(models.Model):
assets = Asset.objects.filter(nodes__in=children)
return assets
@property
def parent(self):
if self.key == "0":
return self.__class__.root()
elif not self.key.startswith("0"):
return self.__class__.root()
parent_key = ":".join(self.key.split(":")[:-1])
try:
parent = self.__class__.objects.get(key=parent_key)
except Node.DoesNotExist:
return self.__class__.root()
else:
return parent
@classmethod
def root(cls):
obj, created = cls.objects.get_or_create(
......
......@@ -328,11 +328,7 @@ class NodeSerializer(serializers.ModelSerializer):
@staticmethod
def get_parent(obj):
if obj.key == "0":
return "#"
if not obj.key.startswith("0"):
return "0"
return ":".join(obj.key.split(":")[:-1])
return obj.parent.id
def get_fields(self):
fields = super().get_fields()
......
......@@ -64,7 +64,6 @@
</div>
</div>
{% include 'assets/_asset_import_modal.html' %}
{#{% include 'assets/_asset_bulk_update_modal.html' %}#}
{% endblock %}
{% block custom_foot_js %}
......@@ -126,15 +125,7 @@ $(document).ready(function(){
})
.on('click', '.labels li', function () {
var val = $(this).text();
{#var origin_val = $("#asset_list_table_filter input").val();#}
{#var new_val;#}
{#if (origin_val === "") {#}
{# new_val = val;#}
{# } else { #}
{# new_val = origin_val + " " + val;#}
{# } #}
$("#asset_list_table_filter input").val(val);
{#$('#asset_list_table').DataTable().search(val).draw();#}
jumpserver.table.search(val).draw();
})
.on('click', '.btn_export', function () {
......
......@@ -5,13 +5,7 @@
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/jstree/style.min.css' %}" rel="stylesheet">
<link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
{# <link href="{% static 'css/plugins/ztree/demo.css' %}" rel="stylesheet">#}
<script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
<script type="text/javascript">
$(document).ready(function(){
});
</script>
<style type="text/css">
div#rMenu {
position:absolute;
......@@ -45,7 +39,6 @@
<div class="ibox float-e-margins">
<div class="ibox-content mailbox-content" style="padding-top: 0">
<div class="file-manager ">
{# <h5>Tree View</h5>#}
<div id="assetTree" class="ztree">
</div>
......@@ -55,9 +48,18 @@
</div>
</div>
<div class="col-lg-9 animated fadeInRight">
<div class="row">
<div class="mail-box-header">
<div class="uc pull-left m-r-5"><a href="{% url "assets:asset-create" %}" class="btn btn-sm btn-primary"> {% trans "Add asset" %} </a></div>
<div class="uc pull-left m-r-5"><a class="btn btn-sm btn-primary btn-create-asset"> {% trans "Create asset" %} </a></div>
<div class="html5buttons">
<div class="dt-buttons btn-group">
<a class="btn btn-default btn_import" data-toggle="modal" data-target="#asset_import_modal" tabindex="0">
<span>{% trans "Import" %}</span>
</a>
<a class="btn btn-default btn_export" tabindex="0">
<span>{% trans "Export" %}</span>
</a>
</div>
</div>
<div class="btn-group" style="float: right">
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>
<ul class="dropdown-menu labels">
......@@ -72,8 +74,6 @@
<th class="text-center"><input type="checkbox" class="ipt_check_all"></th>
<th class="text-center">{% trans 'Hostname' %}</th>
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'Port' %}</th>
<th class="text-center">{% trans 'Cluster' %}</th>
<th class="text-center">{% trans 'Hardware' %}</th>
<th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'Reachable' %}</th>
......@@ -100,28 +100,27 @@
</div>
</div>
</div>
<div class="row">
</div>
</div>
</div>
</div>
<div id="rMenu">
<ul class="dropdown-menu">
<li id="m_del" tabindex="-1" onclick="editTreeNode();"><a>重命名</a></li>
<li id="m_add" tabindex="-1" onclick="addTreeNode();"><a>添加节点</a></li>
<li id="m_add" class="btn-create-asset" tabindex="-1" onclick="addTreeNode();"><a>{% trans 'Create asset' %}</a></li>
<li class="divider"></li>
<li id="m_add" tabindex="-1" onclick="addTreeNode();"><a>{% trans 'Add node' %}</a></li>
<li id="m_del" tabindex="-1" onclick="editTreeNode();"><a>{% trans 'Rename node' %}</a></li>
<li class="divider"></li>
<li id="m_del" tabindex="-1" onclick="removeTreeNode();"><a>删除节点</a></li>
<li id="m_del" tabindex="-1" onclick="removeTreeNode();"><a>{% trans 'Delete node' %}</a></li>
</ul>
</div>
{% include 'assets/_asset_import_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/jstree/jstree.min.js' %}"></script>
<script>
var zTree, rMenu;
function initTable() {
var zTree, rMenu, asset_table;
function initTable() {
var options = {
ele: $('#asset_list_table'),
columnDefs: [
......@@ -130,21 +129,18 @@
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
$(td).html(rowData.cluster_name)
}},
{targets: 5, createdCell: function (td, cellData, rowData) {
{targets: 3, createdCell: function (td, cellData, rowData) {
$(td).html(rowData.hardware_info)
}},
{targets: 6, createdCell: function (td, cellData) {
{targets: 4, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 7, createdCell: function (td, cellData) {
if (cellData == 'Unknown'){
{targets: 5, createdCell: function (td, cellData) {
if (cellData === 'Unknown'){
$(td).html('<i class="fa fa-circle text-warning"></i>')
} else if (!cellData) {
$(td).html('<i class="fa fa-circle text-danger"></i>')
......@@ -152,7 +148,7 @@
$(td).html('<i class="fa fa-circle text-navy"></i>')
}
}},
{targets: 8, createdCell: function (td, cellData, rowData) {
{targets: 6, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:asset-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn)
......@@ -160,70 +156,18 @@
],
ajax_url: '{% url "api-assets:asset-list" %}',
columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
{data: "cluster"},
{data: "id"}, {data: "hostname" }, {data: "ip" },
{data: "cpu_cores"}, {data: "is_active", orderable: false },
{data: "is_connective", orderable: false}, {data: "id", orderable: false }
],
op_html: $('#actions').html()
};
return jumpserver.initServerSideDataTable(options);
}
asset_table = jumpserver.initServerSideDataTable(options);
return asset_table
}
function OnRightClick(event, treeId, treeNode) {
if (!treeNode && event.target.tagName.toLowerCase() !== "button" && $(event.target).parents("a").length == 0) {
zTree.cancelSelectedNode();
showRMenu("root", event.clientX, event.clientY);
} else if (treeNode && !treeNode.noR) {
zTree.selectNode(treeNode);
showRMenu("node", event.clientX, event.clientY);
}
}
function showRMenu(type, x, y) {
$("#rMenu ul").show();
if (type === "root") {
return
} else {
$("#m_del").show();
$("#m_check").show();
$("#m_unCheck").show();
}
{#y += $("#page-wrapper")[0].scrollTop;#}
{#x += $("#page-wrapper")[0].scrollLeft;#}
x -= 220;
{#y -= 100;#}
{#y += document.body.scrollTop;#}
{#x += document.body.scrollLeft;#}
rMenu.css({"top":y+"px", "left":x+"px", "visibility":"visible"});
$("body").bind("mousedown", onBodyMouseDown);
}
function beforeClick(treeId, treeNode, clickFlag) {
return true;
}
function onClick(event, treeId, treeNode, clickFlag) {
showLog("On click");
}
function showLog(str) {
console.log(str)
}
function hideRMenu() {
if (rMenu) rMenu.css({"visibility": "hidden"});
$("body").unbind("mousedown", onBodyMouseDown);
}
function onBodyMouseDown(event){
if (!(event.target.id === "rMenu" || $(event.target).parents("#rMenu").length>0)) {
rMenu.css({"visibility" : "hidden"});
}
}
function addTreeNode() {
function addTreeNode() {
hideRMenu();
var parentNode = zTree.getSelectedNodes()[0];
if (!parentNode){
......@@ -232,16 +176,20 @@
var url = "{% url 'api-assets:node-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", parentNode.id );
$.post(url, {}, function (data, status){
if (status === "success") {
var newNode = { name:data["name"], id:data["id"], pId: parentNode.id };
var newNode = {
name: data["value"],
id: data["id"],
pId: parentNode.id
};
newNode.checked = zTree.getSelectedNodes()[0].checked;
zTree.addNodes(parentNode, 0, newNode);
} else {
alert("{% trans 'Create node failed' %}")
}
});
}
}
function removeTreeNode() {
function removeTreeNode() {
hideRMenu();
var current_node = zTree.getSelectedNodes()[0];
if (!current_node){
......@@ -251,7 +199,7 @@
if (current_node.children && current_node.children.length > 0) {
alert("{% trans 'Have child node, cancel' %}")
} else {
var url = "{% url 'api-assets:node-detail' pk='0:0' %}".replace("0:0", current_node.id );
var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id );
$.ajax({
url: url,
method: "DELETE",
......@@ -260,9 +208,60 @@
}
});
}
}
function editTreeNode() {
hideRMenu();
var current_node = zTree.getSelectedNodes()[0];
if (!current_node){
return
}
zTree.editName(current_node);
}
function OnRightClick(event, treeId, treeNode) {
if (!treeNode && event.target.tagName.toLowerCase() !== "button" && $(event.target).parents("a").length === 0) {
zTree.cancelSelectedNode();
showRMenu("root", event.clientX, event.clientY);
} else if (treeNode && !treeNode.noR) {
zTree.selectNode(treeNode);
showRMenu("node", event.clientX, event.clientY);
}
}
function showRMenu(type, x, y) {
$("#rMenu ul").show();
if (type === "root") {
return
} else {
$("#m_del").show();
$("#m_check").show();
$("#m_unCheck").show();
}
x -= 220;
rMenu.css({"top":y+"px", "left":x+"px", "visibility":"visible"});
$("body").bind("mousedown", onBodyMouseDown);
}
function beforeClick(treeId, treeNode, clickFlag) {
return true;
}
function onRenameNode(event, treeId, treeNode, isCancel){
function hideRMenu() {
if (rMenu) rMenu.css({"visibility": "hidden"});
$("body").unbind("mousedown", onBodyMouseDown);
}
function onBodyMouseDown(event){
if (!(event.target.id === "rMenu" || $(event.target).parents("#rMenu").length>0)) {
rMenu.css({"visibility" : "hidden"});
}
}
function onRename(event, treeId, treeNode, isCancel){
var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", treeNode.id);
var data = {"value": treeNode.name};
if (isCancel){
......@@ -273,18 +272,26 @@
body: JSON.stringify(data),
method: "PATCH"
})
}
}
function editTreeNode() {
hideRMenu();
var current_node = zTree.getSelectedNodes()[0];
if (!current_node){
return
function onSelected(event, treeNode) {
var url = asset_table.ajax.url();
url = setUrlParam(url, "node_id", treeNode.id);
asset_table.ajax.url(url);
asset_table.ajax.reload();
}
function selectQueryNode() {
var node_id = $.getUrlParam("node");
if (node_id !== null) {
var node = zTree.getNodesByParam("id", node_id, null);
if (node){
zTree.selectNode(node[0]);
}
zTree.editName(current_node);
}
}
function initTree() {
function initTree() {
var setting = {
view: {
dblClickExpand: false,
......@@ -298,8 +305,8 @@
callback: {
onRightClick: OnRightClick,
beforeClick: beforeClick,
onClick: onClick,
onRename: onRenameNode
onRename: onRename,
onSelected: onSelected
}
};
......@@ -316,12 +323,175 @@
$.fn.zTree.init($("#assetTree"), setting, zNodes);
zTree = $.fn.zTree.getZTreeObj("assetTree");
rMenu = $("#rMenu");
selectQueryNode();
});
}
$(document).ready(function(){
}
$(document).ready(function(){
initTable();
initTree();
})
.on('click', '.labels li', function () {
var val = $(this).text();
$("#asset_list_table_filter input").val(val);
jumpserver.table.search(val).draw();
})
.on('click', '.btn_export', function () {
var $data_table = $('#asset_list_table').DataTable();
var rows = $data_table.rows('.selected').data();
var assets = [];
$.each(rows, function (index, obj) {
assets.push(obj.id)
});
$.ajax({
url: "{% url "assets:asset-export" %}",
method: 'POST',
data: JSON.stringify({assets_id: assets}),
dataType: "json",
success: function (data, textStatus) {
window.open(data.redirect)
},
error: function () {
toastr.error('Export failed');
}
})
})
.on('click', '#btn_asset_import', function () {
var $form = $('#fm_asset_import');
$form.find('.help-block').remove();
function success (data) {
if (data.valid === false) {
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_assets'));
} else {
$('#id_created').html(data.created_info);
$('#id_created_detail').html(data.created.join(', '));
$('#id_updated').html(data.updated_info);
$('#id_updated_detail').html(data.updated.join(', '));
$('#id_failed').html(data.failed_info);
$('#id_failed_detail').html(data.failed.join(', '));
var $data_table = $('#asset_list_table').DataTable();
$data_table.ajax.reload();
}
}
$form.ajaxSubmit({success: success});
})
.on('click', '.btn-create-asset', function () {
var url = "{% url 'assets:asset-create' %}";
var nodes = zTree.getSelectedNodes();
var current_node;
if (nodes && nodes.length ===1 ){
current_node = nodes[0];
url += "?node_id=" + current_node.id;
}
window.open(url);
})
.on('click', '.btn_asset_delete', function () {
var $this = $(this);
var $data_table = $("#asset_list_table").DataTable();
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
var uid = $this.data('uid');
var the_url = '{% url "api-assets:asset-detail" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", uid);
objectDelete($this, name, the_url);
setTimeout( function () {
$data_table.ajax.reload();
}, 3000);
})
.on('click', '#btn_bulk_update', function () {
var action = $('#slct_bulk_update').val();
var $data_table = $('#asset_list_table').DataTable();
var id_list = [];
$data_table.rows({selected: true}).every(function(){
id_list.push(this.data().id);
});
if (id_list.length === 0) {
return false;
}
var the_url = "{% url 'api-assets:asset-list' %}";
function doDeactive() {
var data = [];
$.each(id_list, function(index, object_id) {
var obj = {"pk": object_id, "is_active": false};
data.push(obj);
});
function success() {
asset_table.ajax.reload()
}
APIUpdateAttr({
url: the_url,
method: 'PATCH',
body: JSON.stringify(data),
success: success
});
}
function doActive() {
var data = [];
$.each(id_list, function(index, object_id) {
var obj = {"pk": object_id, "is_active": true};
data.push(obj);
});
function success() {
asset_table.ajax.reload()
}
APIUpdateAttr({
url: the_url,
method: 'PATCH',
body: JSON.stringify(data),
success: success
});
}
function doDelete() {
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected assets !!!' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
var success = function() {
var msg = "{% trans 'Asset Deleted.' %}";
swal("{% trans 'Asset Delete' %}", msg, "success");
$('#asset_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'Asset Deleting failed.' %}";
swal("{% trans 'Asset Delete' %}", msg, "error");
};
var url_delete = the_url + '?id__in=' + JSON.stringify(id_list);
APIUpdateAttr({
url: url_delete,
method: 'DELETE',
success: success,
error: fail
});
$data_table.ajax.reload();
jumpserver.checked = false;
});
}
function doUpdate() {
var id_list_string = id_list.join(',');
var url = "{% url 'assets:asset-bulk-update' %}?assets_id=" + id_list_string;
location.href = url
}
switch(action) {
case 'deactive':
doDeactive();
break;
case 'delete':
doDelete();
break;
case 'update':
doUpdate();
break;
case 'active':
doActive();
break;
default:
break;
}
});
</script>
{% endblock %}
\ No newline at end of file
......@@ -27,7 +27,7 @@ from common.mixins import JSONResponseMixin
from common.utils import get_object_or_none, get_logger, is_uuid
from common.const import create_success_msg, update_success_msg
from .. import forms
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser, Label
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser, Label, Node
from ..hands import AdminUserRequiredMixin
......@@ -41,13 +41,12 @@ logger = get_logger(__file__)
class AssetListView(AdminUserRequiredMixin, TemplateView):
template_name = 'assets/asset_list.html'
template_name = 'assets/tree.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Asset list'),
'system_users': SystemUser.objects.all(),
'labels': Label.objects.all().order_by('name'),
}
kwargs.update(context)
......@@ -81,6 +80,16 @@ class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
# asset.save()
# return super().form_valid(form)
def get_form(self, form_class=None):
form = super().get_form(form_class=form_class)
node_id = self.request.GET.get("node_id")
if node_id:
node = get_object_or_none(Node, id=node_id)
else:
node = Node.root()
form["nodes"].initial = node
return form
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
......
......@@ -5,6 +5,7 @@ from django.views.generic import TemplateView
from django.utils.translation import ugettext_lazy as _
from common.mixins import AdminUserRequiredMixin
from ..models import Label
__all__ = ['TreeView']
......@@ -17,6 +18,7 @@ class TreeView(AdminUserRequiredMixin, TemplateView):
context = {
'app': _('Assets'),
'action': _('Tree view'),
'labels': Label.objects.all(),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
......
......@@ -47,9 +47,9 @@ class JSONResponseMixin(object):
return JsonResponse(context)
class CustomFilterMixin(object):
class IDInFilterMixin(object):
def filter_queryset(self, queryset):
queryset = super(CustomFilterMixin, self).filter_queryset(queryset)
queryset = super(IDInFilterMixin, self).filter_queryset(queryset)
id_list = self.request.query_params.get('id__in')
if id_list:
import json
......
......@@ -9,10 +9,9 @@ from rest_framework import viewsets
from common.utils import get_object_or_none
from users.permissions import IsValidUser, IsSuperUser, IsAppUser, IsSuperUserOrAppUser
from .utils import get_user_granted_assets, get_user_granted_asset_groups, \
get_user_asset_permissions, get_user_group_asset_permissions, \
get_user_group_granted_assets, get_user_group_granted_asset_groups
from .models import AssetPermission, NodePermission
from .hands import AssetGrantedSerializer, User, UserGroup, AssetGroup, Asset, \
from .hands import AssetGrantedSerializer, User, UserGroup, Node, Asset, \
AssetGroup, AssetGroupGrantedSerializer, SystemUser, MyAssetGroupGrantedSerializer
from . import serializers
......@@ -30,24 +29,12 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
return serializers.AssetPermissionListSerializer
return self.serializer_class
# def get_queryset(self):
# queryset = super(AssetPermissionViewSet, self).get_queryset()
# user_id = self.request.query_params.get('user', '')
# user_group_id = self.request.query_params.get('user_group', '')
#
# if user_id and user_id.isdigit():
# user = get_object_or_404(User, id=int(user_id))
# queryset = get_user_asset_permissions(user)
#
# if user_group_id:
# user_group = get_object_or_404(UserGroup, id=user_group_id)
# queryset = get_user_group_asset_permissions(user_group)
# return queryset
# def get_serializer_class(self):
# if getattr(self, 'user_id', ''):
# return serializers.UserAssetPermissionCreateUpdateSerializer
# return serializers.AssetPermissionCreateUpdateSerializer
def get_queryset(self):
queryset = super().get_queryset()
node_id = self.request.query_params.get('node_id')
if node_id:
queryset = queryset.filter(node__id=node_id)
return queryset
class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView):
......
......@@ -3,7 +3,7 @@
from users.utils import AdminUserRequiredMixin
from users.models import User, UserGroup
from assets.models import Asset, AssetGroup, SystemUser
from assets.models import Asset, AssetGroup, SystemUser, Node
from assets.serializers import AssetGrantedSerializer, AssetGroupGrantedSerializer, MyAssetGroupGrantedSerializer
......
{% extends '_base_list.html' %}
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block table_search %}
{% endblock %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/jstree/style.min.css' %}" rel="stylesheet">
<link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
<script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
<style type="text/css">
div#rMenu {
position:absolute;
visibility:hidden;
text-align: left;
top: 100%;
left: 0;
z-index: 1000;
float: left;
padding: 5px 0;
margin: 2px 0 0;
list-style: none;
background-clip: padding-box;
}
div#rMenu li{
margin: 1px 0;
cursor: pointer;
{#list-style: none outside none;#}
}
.dropdown a:hover {
background-color: #f1f1f1
}
</style>
{% block help_message %}
<div class="alert alert-info help-message">
</div>
{% endblock %}
{% block table_container %}
<div class="uc pull-left m-r-5">
<a href="{% url 'perms:asset-permission-create' %}" class="btn btn-sm btn-primary ">
{% block content %}
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-lg-3">
<div class="ibox float-e-margins">
<div class="ibox-content mailbox-content" style="padding-top: 0">
<div class="file-manager ">
<div id="assetTree" class="ztree">
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
<div class="col-lg-9 animated fadeInRight">
<div class="mail-box-header">
<div class="uc pull-left m-r-5">
<a class="btn btn-sm btn-primary btn-create-permission">
{% trans "Create permission" %}
</a>
</div>
<table class="table table-striped table-bordered table-hover" id="permission_list_table" >
</div>
<table class="table table-striped table-bordered table-hover" id="permission_list_table" >
<thead>
<tr>
<th class="text-center">
......@@ -31,26 +69,32 @@
</thead>
<tbody>
</tbody>
</table>
</table>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
var zTree, rMenu, table;
function initTable() {
var options = {
ele: $('#permission_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData) {
var html = '<a href="">' + cellData.name + '</a>';
$(td).html(html)
var html = '<a href="{% url 'assets:tree-view' %}?node=99899">' + cellData.name + '</a>';
$(td).html(html.replace("99899", cellData.pk));
}},
{targets: 2, createdCell: function (td, cellData) {
var html = '<a href="">' + cellData.name + '</a>';
$(td).html(html)
var html = '<a href="{% url "users:user-group-detail" pk=DEFAULT_PK %}">' + cellData.name + '</a>';
$(td).html(html.replace("{{ DEFAULT_PK }}", cellData.pk))
}},
{targets: 3, createdCell: function (td, cellData) {
var html = '<a href="">' + cellData.name + '</a>';
$(td).html(html)
var html = '<a href="{% url 'assets:system-user-detail' pk=DEFAULT_PK %}">' + cellData.name + '</a>';
$(td).html(html.replace("{{ DEFAULT_PK }}", cellData.pk));
}},
{targets: 4, createdCell: function (td, cellData) {
if (!cellData) {
......@@ -59,7 +103,14 @@ function initTable() {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 5, createdCell: function (td, cellData) {
var date_expired = cellData.split(" ");
if (date_expired && date_expired.length === 3) {
$(td).html(date_expired[0]);
} else {
$(td).html(cellData);
}
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "perms:asset-permission-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-del" data-uid="{{ DEFAULT_PK }}" data-name="99991938">{% trans "Delete" %}</a>'
......@@ -76,13 +127,75 @@ function initTable() {
],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options)
table = jumpserver.initDataTable(options);
return table
}
function onSelected(event, treeNode) {
var url = table.ajax.url();
url = setUrlParam(url, "node_id", treeNode.id);
table.ajax.url(url);
table.ajax.reload();
}
function selectQueryNode() {
var node_id = $.getUrlParam("node");
if (node_id !== null) {
var node = zTree.getNodesByParam("id", node_id, null);
if (node) {
zTree.selectNode(node[0]);
}
}
}
function initTree() {
var setting = {
view: {
dblClickExpand: false,
showLine: true
},
data: {
simpleData: {
enable: true
}
},
callback: {
onSelected: onSelected
}
};
var zNodes = [];
$.get("{% url 'api-assets:node-list' %}", function(data, status){
$.each(data, function (index, value) {
value["pId"] = value["parent"];
if (value["key"] === "0") {
value["open"] = true;
}
value["name"] = value["value"]
});
zNodes = data;
$.fn.zTree.init($("#assetTree"), setting, zNodes);
zTree = $.fn.zTree.getZTreeObj("assetTree");
rMenu = $("#rMenu");
selectQueryNode();
});
}
$(document).ready(function(){
initTable()
initTable();
initTree();
})
.on('click', '.btn-create-asset', function () {
var url = "{% url 'assets:asset-create' %}";
var nodes = zTree.getSelectedNodes();
var current_node;
if (nodes && nodes.length ===1 ){
current_node = nodes[0];
url += "?node=" + current_node.id;
}
window.open(url);
})
.on('click', '.btn-del', function () {
var $this = $(this);
var name = $this.data('name');
......@@ -91,5 +204,16 @@ $(document).ready(function(){
.replace('{{ DEFAULT_PK }}', uid);
objectDelete($this, name, the_url);
})
.on('click', '.btn-create-permission', function () {
var url = "{% url 'perms:asset-permission-create' %}";
var nodes = zTree.getSelectedNodes();
var current_node;
if (nodes && nodes.length ===1 ){
current_node = nodes[0];
url += "?node_id=" + current_node.id;
}
window.open(url);
})
</script>
{% endblock %}
......@@ -10,9 +10,9 @@ from django.urls import reverse_lazy
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.detail import DetailView, SingleObjectMixin
from common.const import create_success_msg, update_success_msg
from common.utils import get_object_or_none
from .hands import AdminUserRequiredMixin, User, UserGroup, SystemUser, \
Asset, AssetGroup
Asset, AssetGroup, Node
from .models import AssetPermission, NodePermission
from .forms import AssetPermissionForm
......@@ -37,6 +37,15 @@ class AssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
template_name = 'perms/asset_permission_create_update.html'
success_url = reverse_lazy('perms:asset-permission-list')
def get_form(self, form_class=None):
form = super().get_form(form_class=form_class)
node_id = self.request.GET.get("node_id")
node = get_object_or_none(Node, id=node_id)
if not node:
node = Node.root()
form['node'].initial = node
return form
def get_context_data(self, **kwargs):
context = {
'app': _('Perms'),
......
......@@ -531,3 +531,43 @@ function createPopover(dataset, title, callback) {
var html = "<a data-toggle='popover' data-content='" + data_content + "'>" + dataset.length + "</a>";
return html;
}
$(function () {
(function ($) {
$.getUrlParam = function (name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]); return null;
}
})(jQuery);
});
function getUrlParam(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]); return null;
}
function setUrlParam(url, name, value) {
var urlArray = url.split("?");
if (urlArray.length===1){
url += "?" + name + "=" + value;
} else {
var oriParam = urlArray[1].split("&");
var oriParamMap = {};
$.each(oriParam, function (index, value) {
var v = value.split("=");
oriParamMap[v[0]] = v[1];
});
oriParamMap[name] = value;
url = urlArray[0] + "?";
var newParam = [];
$.each(oriParamMap, function (index, value) {
console.log(index, value);
newParam.push(index + "=" + value);
});
url += newParam.join("&")
}
return url
}
......@@ -20,8 +20,6 @@
</a>
<ul class="nav nav-second-level">
<li id="asset"><a href="{% url 'assets:asset-list' %}">{% trans 'Asset' %}</a></li>
<li id="asset-group"><a href="{% url 'assets:asset-group-list' %}">{% trans 'Asset group' %}</a></li>
<li id="cluster"><a href="{% url 'assets:cluster-list' %}">{% trans 'Cluster' %}</a></li>
<li id="admin-user"><a href="{% url 'assets:admin-user-list' %}">{% trans 'Admin user' %}</a></li>
<li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li>
<li id="label"><a href="{% url 'assets:label-list' %}">{% trans 'Labels' %}</a></li>
......
......@@ -17,14 +17,14 @@ from .models import User, UserGroup
from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly, \
IsSuperUserOrAppUser
from .utils import check_user_valid, generate_token
from common.mixins import CustomFilterMixin
from common.mixins import IDInFilterMixin
from common.utils import get_logger
logger = get_logger(__name__)
class UserViewSet(CustomFilterMixin, BulkModelViewSet):
class UserViewSet(IDInFilterMixin, BulkModelViewSet):
queryset = User.objects.exclude(role="App")
# queryset = User.objects.all().exclude(role="App").order_by("date_joined")
serializer_class = UserSerializer
......@@ -88,7 +88,7 @@ class UserUpdatePKApi(generics.UpdateAPIView):
user.save()
class UserGroupViewSet(CustomFilterMixin, BulkModelViewSet):
class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet):
queryset = UserGroup.objects.all()
serializer_class = UserGroupSerializer
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册