diff --git a/apps/assets/api/node.py b/apps/assets/api/node.py index d7499760a..f123e4649 100644 --- a/apps/assets/api/node.py +++ b/apps/assets/api/node.py @@ -32,6 +32,7 @@ __all__ = [ 'NodeViewSet', 'NodeChildrenApi', 'NodeAssetsApi', 'NodeWithAssetsApi', 'NodeAddAssetsApi', 'NodeRemoveAssetsApi', + 'NodeReplaceAssetsApi', 'NodeAddChildrenApi', 'RefreshNodeHardwareInfoApi', 'TestNodeConnectiveApi' ] @@ -191,6 +192,19 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView): instance.assets.remove(*tuple(assets)) +class NodeReplaceAssetsApi(generics.UpdateAPIView): + serializer_class = serializers.NodeAssetsSerializer + queryset = Node.objects.all() + permission_classes = (IsSuperUser,) + instance = None + + def perform_update(self, serializer): + assets = serializer.validated_data.get('assets') + instance = self.get_object() + for asset in assets: + asset.nodes.set([instance]) + + class RefreshNodeHardwareInfoApi(APIView): permission_classes = (IsSuperUser,) model = Node diff --git a/apps/assets/templates/assets/_add_assets_to_node_modal.html b/apps/assets/templates/assets/_add_assets_to_node_modal.html new file mode 100644 index 000000000..31641e722 --- /dev/null +++ b/apps/assets/templates/assets/_add_assets_to_node_modal.html @@ -0,0 +1,7 @@ +{% extends 'assets/_asset_list_modal.html' %} +{% load i18n %} + +{% block modal_button %} + + +{% endblock %} \ No newline at end of file diff --git a/apps/assets/templates/assets/_asset_list_modal.html b/apps/assets/templates/assets/_asset_list_modal.html index b68ab0378..a4f2c7f77 100644 --- a/apps/assets/templates/assets/_asset_list_modal.html +++ b/apps/assets/templates/assets/_asset_list_modal.html @@ -1,132 +1,126 @@ {% extends '_modal.html' %} {% load i18n %} +{% load static %} {% block modal_class %}modal-lg{% endblock %} {% block modal_id %}asset_list_modal{% endblock %} -{#{% block modal_title%}{% trans "Please select assets" %}{% endblock %}#} +{% block modal_title%}{% trans "Asset list" %}{% endblock %} {% block modal_body %} -{#
#} -{# #} -{# #} -{#
#} - - - - - - - - - - - - - - -
{% trans 'Hostname' %}{% trans 'IP' %}{% trans 'Hardware' %}{% trans 'Active' %}{% trans 'Reachable' %}{% trans 'Action' %}
-
-
- -
- + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + +
{% trans 'Hostname' %}{% trans 'IP' %}
+
+ - {% endblock %} -{% block modal_confirm_id %}btn_select_assets{% endblock %} + +{% block modal_button %} + {{ block.super }} +{% endblock %} +{% block modal_confirm_id %}btn_asset_modal_confirm{% endblock %} + diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index 336f4c217..8e3710624 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -130,7 +130,7 @@
{% include 'assets/_asset_import_modal.html' %} -{% include 'assets/_asset_list_modal.html' %} +{% include 'assets/_add_assets_to_node_modal.html' %} {% endblock %} {% block custom_foot_js %} @@ -210,10 +210,11 @@ function removeTreeNode() { if (!current_node){ return } - if (current_node.children && current_node.children.length > 0) { - alert("{% trans 'Have child node, cancel' %}") - } else { + toastr.error("{% trans 'Have child node, cancel' %}"); + } else if (current_node.assets_amount !== 0) { + toastr.error("{% trans 'Have assets, cancel' %}"); + } else { var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id ); $.ajax({ url: url, @@ -249,13 +250,6 @@ function OnRightClick(event, treeId, treeNode) { 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"}); @@ -459,7 +453,7 @@ $(document).ready(function(){ var current_node; if (nodes && nodes.length ===1 ){ current_node = nodes[0]; - action = setUrlParam(action, 'node_id', current_node.id) + action = setUrlParam(action, 'node_id', current_node.id); {#action += "?node_id=" + current_node.id;#} $form.attr("action", action) } @@ -674,7 +668,42 @@ $(document).ready(function(){ break; } $(".ipt_check_all").prop("checked", false) -}); +}) +.on('click', '.btn-node-update', function () { + var assets_selected = asset_table2.selected; + var current_node; + var nodes = zTree.getSelectedNodes(); + if (nodes && nodes.length === 1) { + current_node = nodes[0] + } else { + return + } + + var data = { + 'assets': assets_selected + }; + var success = function () { + asset_table2.selected = []; + asset_table2.ajax.reload() + }; + + var btn_id = $(this).attr('id'); + var url = ''; + if (btn_id === "btn_move") { + url = "{% url 'api-assets:node-replace-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id); + } else { + url = "{% url 'api-assets:node-add-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id); + } + + APIUpdateAttr({ + 'url': url, + 'method': 'PUT', + 'body': JSON.stringify(data), + 'success': success + }) +}).on('hidden.bs.modal', '#asset_list_modal', function () { + window.location.reload(); +}) {% endblock %} \ No newline at end of file diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py index c4925059a..4429d0f24 100644 --- a/apps/assets/urls/api_urls.py +++ b/apps/assets/urls/api_urls.py @@ -40,6 +40,7 @@ urlpatterns = [ url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/children/add/$', api.NodeAddChildrenApi.as_view(), name='node-add-children'), url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/$', api.NodeAssetsApi.as_view(), name='node-assets'), url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/add/$', api.NodeAddAssetsApi.as_view(), name='node-add-assets'), + url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/replace/$', api.NodeReplaceAssetsApi.as_view(), name='node-replace-assets'), url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/remove/$', api.NodeRemoveAssetsApi.as_view(), name='node-remove-assets'), url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/refresh-hardware-info/$', api.RefreshNodeHardwareInfoApi.as_view(), name='node-refresh-hardware-info'), url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/test-connective/$', api.TestNodeConnectiveApi.as_view(), name='node-test-connective'), diff --git a/apps/assets/views/asset.py b/apps/assets/views/asset.py index 956228f89..306a26a0a 100644 --- a/apps/assets/views/asset.py +++ b/apps/assets/views/asset.py @@ -49,6 +49,7 @@ class AssetListView(AdminUserRequiredMixin, TemplateView): 'app': _('Assets'), 'action': _('Asset list'), 'labels': Label.objects.all().order_by('name'), + 'nodes': Node.objects.all().order_by('-key'), } kwargs.update(context) return super().get_context_data(**kwargs) diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py index e98d17dbc..e8a490bf3 100644 --- a/apps/jumpserver/urls.py +++ b/apps/jumpserver/urls.py @@ -36,11 +36,12 @@ urlpatterns = [ url(r'^captcha/', include('captcha.urls')), ] -urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) \ - + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) +urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) if settings.DEBUG: urlpatterns += [ url(r'^docs/', schema_view, name="docs"), ] +if not settings.DEBUG: + urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index f2f8b8f55..461455085 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -307,7 +307,7 @@ jumpserver.initDataTable = function (options) { last: "»" } }, - lengthMenu: [[15, 25, 50, -1], [15, 25, 50, "All"]] + lengthMenu: [[10, 15, 25, 50, -1], [10, 15, 25, 50, "All"]] }); table.on('select', function(e, dt, type, indexes) { var $node = table[ type ]( indexes ).nodes().to$(); @@ -446,22 +446,56 @@ jumpserver.initServerSideDataTable = function (options) { last: "»" } }, - lengthMenu: [[15, 25, 50], [15, 25, 50]] + lengthMenu: [[10, 15, 25, 50], [10, 15, 25, 50]] }); + table.selected = []; table.on('select', function(e, dt, type, indexes) { var $node = table[ type ]( indexes ).nodes().to$(); $node.find('input.ipt_check').prop('checked', true); - jumpserver.selected[$node.find('input.ipt_check').prop('id')] = true + jumpserver.selected[$node.find('input.ipt_check').prop('id')] = true; + if (type === 'row') { + var rows = table.rows(indexes).data(); + $.each(rows, function (id, row) { + if (row.id){ + table.selected.push(row.id) + } + }) + } }).on('deselect', function(e, dt, type, indexes) { var $node = table[ type ]( indexes ).nodes().to$(); $node.find('input.ipt_check').prop('checked', false); - jumpserver.selected[$node.find('input.ipt_check').prop('id')] = false + jumpserver.selected[$node.find('input.ipt_check').prop('id')] = false; + if (type === 'row') { + var rows = table.rows(indexes).data(); + $.each(rows, function (id, row) { + if (row.id){ + var index = table.selected.indexOf(row.id); + if (index > -1){ + table.selected.splice(index, 1) + } + } + }) + } }). on('draw', function(){ $('#op').html(options.op_html || ''); $('#uc').html(options.uc_html || ''); + var table_data = []; + $.each(table.rows().data(), function (id, row) { + if (row.id) { + table_data.push(row.id) + } + }); + + $.each(table.selected, function (id, data) { + var index = table_data.indexOf(data); + if (index > -1){ + table.rows(index).select() + } + }); }); - $('.ipt_check_all').on('click', function() { + var table_id = table.settings()[0].sTableId; + $('#' + table_id + ' .ipt_check_all').on('click', function() { if ($(this).prop("checked")) { $(this).closest('table').find('.ipt_check').prop('checked', true); table.rows({search:'applied', page:'current'}).select(); diff --git a/apps/templates/_modal.html b/apps/templates/_modal.html index ad7f21ccc..7705b8660 100644 --- a/apps/templates/_modal.html +++ b/apps/templates/_modal.html @@ -12,8 +12,10 @@ {% endblock %}