Commit 22f362aa authored by 八千流's avatar 八千流 Committed by 老广

Dev csv (#2640)

* [Update] 封装JMSCSVRender和JMSCSVParser

* [Update] 更改JMSCSVRender,根据请求参数控制导出csv的字段和下载csv模板的字段

* [Update] 导入空数据,提示错误消息

* [Update] 修改用户导入和导出功能代码

* [Update] 修改导入路由为动态反向解析

* [Update] 修改JMSCSVRender和JMSCSVParser以及用户导入导出代码

* [Update] 优化parsers逻辑

* [Update] 优化parsers csv代码结构

* [Update] 优化renders csv代码逻辑

* [Update] 删除parsers csv多余代码

* [Update] 删除parsers csv多余变量

* [Update] 优化renders csv代码结构

* [Update] 优化renders csv代码结构2

* [Update] 优化renders csv获取header逻辑

* [Update] 优化Cache Resources ID View逻辑

* [Update] 优化ViewSet IDCacheFilterMixin逻辑

* [Update] csv: parser render 添加异常捕获逻辑

* [Update] 删除多余代码

* [Update] 优化前端代码

* [Update] 修改小问题

* [Update] 修改前端导出用户的问题

* [Update] 前端 - 优化数据导出逻辑 APIExportData

* [Update] 修复批量创建用户时发送created信号的bug

* [Update] 优化导入时错误信息展示

* [Update] 优化parser、render时,对于多对多字段的处理

* [Update] 修改前端上传空文件问题

* [Update] 添加IDExportFilter,控制下载模版时的queryset

* [Update] 修改判断导出模版时参数变量名 action => template

* [Update] 修复导入用户数据时,用户组不生效的bug

* [Update] 修改前端导入信息展示

* [Update] 抽象资源导入模版

* [Update] 优化资源导入模版

* [Update] 修改js设置url的params逻辑

* [Update] 修改users序列类控制read_only字段方式

* [Update] 资产列表采用新的导入/导出csv文件逻辑

* [Update] 修改导入资产时设置资产所在节点逻辑

* [Update] 添加用户组导入/导出功能

* [Update] 修改前端变量名

* [Update] 修改下载导入模版,不包含org字段

* [Update] 增加管理用户导入/导出功能

* [Update] 导入模版提供id字段(为了资源备份后导入直接使用); 修复资源导入时联合唯一字段不校验导致创建时报错的bug

* [Update] 增加系统用户导入/导出功能

* [Update] 排序资源导入/导出字段

* [Update] 翻译导入/导出的字段和模版

* [Update] 更改csv导出和导出模版数据的控制在render实现

* [Update] 资产添加 更新导入 功能

* [Update] 用户/用户组/管理用户/系统用户/ 添加导入更新

* [Update] 翻译

* [Update] 优化资源序列化中的label

* [Update] 去掉资源IDInFilterMixin过滤

* [Update] 翻译
parent 49429008
...@@ -20,7 +20,7 @@ from rest_framework.response import Response ...@@ -20,7 +20,7 @@ from rest_framework.response import Response
from rest_framework_bulk import BulkModelViewSet from rest_framework_bulk import BulkModelViewSet
from rest_framework.pagination import LimitOffsetPagination from rest_framework.pagination import LimitOffsetPagination
from common.mixins import IDInFilterMixin from common.mixins import IDInCacheFilterMixin
from common.utils import get_logger from common.utils import get_logger
from ..hands import IsOrgAdmin from ..hands import IsOrgAdmin
from ..models import AdminUser, Asset from ..models import AdminUser, Asset
...@@ -36,7 +36,7 @@ __all__ = [ ...@@ -36,7 +36,7 @@ __all__ = [
] ]
class AdminUserViewSet(IDInFilterMixin, BulkModelViewSet): class AdminUserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
""" """
Admin user api set, for add,delete,update,list,retrieve resource Admin user api set, for add,delete,update,list,retrieve resource
""" """
......
...@@ -16,8 +16,9 @@ from django.urls import reverse_lazy ...@@ -16,8 +16,9 @@ from django.urls import reverse_lazy
from django.core.cache import cache from django.core.cache import cache
from django.db.models import Q from django.db.models import Q
from common.mixins import IDInFilterMixin from common.mixins import IDInCacheFilterMixin
from common.utils import get_logger
from common.utils import get_logger, get_object_or_none
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
from ..const import CACHE_KEY_ASSET_BULK_UPDATE_ID_PREFIX from ..const import CACHE_KEY_ASSET_BULK_UPDATE_ID_PREFIX
from ..models import Asset, AdminUser, Node from ..models import Asset, AdminUser, Node
...@@ -35,7 +36,7 @@ __all__ = [ ...@@ -35,7 +36,7 @@ __all__ = [
] ]
class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet): class AssetViewSet(IDInCacheFilterMixin, LabelFilter, BulkModelViewSet):
""" """
API endpoint that allows Asset to be viewed or edited. API endpoint that allows Asset to be viewed or edited.
""" """
...@@ -47,6 +48,19 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet): ...@@ -47,6 +48,19 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet):
pagination_class = LimitOffsetPagination pagination_class = LimitOffsetPagination
permission_classes = (IsOrgAdminOrAppUser,) permission_classes = (IsOrgAdminOrAppUser,)
def set_assets_node(self, assets):
if not isinstance(assets, list):
assets = [assets]
node = Node.objects.get(value='Default')
node_id = self.request.query_params.get('node_id')
if node_id:
node = get_object_or_none(Node, pk=node_id)
node.assets.add(*assets)
def perform_create(self, serializer):
assets = serializer.save()
self.set_assets_node(assets)
def filter_node(self, queryset): def filter_node(self, queryset):
node_id = self.request.query_params.get("node_id") node_id = self.request.query_params.get("node_id")
if not node_id: if not node_id:
...@@ -89,7 +103,7 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet): ...@@ -89,7 +103,7 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet):
return queryset return queryset
class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView): class AssetListUpdateApi(IDInCacheFilterMixin, ListBulkCreateUpdateDestroyAPIView):
""" """
Asset bulk update api Asset bulk update api
""" """
......
...@@ -21,6 +21,7 @@ from rest_framework.pagination import LimitOffsetPagination ...@@ -21,6 +21,7 @@ from rest_framework.pagination import LimitOffsetPagination
from common.utils import get_logger from common.utils import get_logger
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
from common.mixins import IDInCacheFilterMixin
from ..models import SystemUser, Asset from ..models import SystemUser, Asset
from .. import serializers from .. import serializers
from ..tasks import push_system_user_to_assets_manual, \ from ..tasks import push_system_user_to_assets_manual, \
...@@ -38,7 +39,7 @@ __all__ = [ ...@@ -38,7 +39,7 @@ __all__ = [
] ]
class SystemUserViewSet(BulkModelViewSet): class SystemUserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
""" """
System user api set, for add,delete,update,list,retrieve resource System user api set, for add,delete,update,list,retrieve resource
""" """
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.core.cache import cache from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from common.serializers import AdaptedBulkListSerializer from common.serializers import AdaptedBulkListSerializer
...@@ -15,14 +16,29 @@ class AdminUserSerializer(serializers.ModelSerializer): ...@@ -15,14 +16,29 @@ class AdminUserSerializer(serializers.ModelSerializer):
""" """
管理用户 管理用户
""" """
assets_amount = serializers.SerializerMethodField() password = serializers.CharField(
unreachable_amount = serializers.SerializerMethodField() required=False, write_only=True, label=_('Password')
reachable_amount = serializers.SerializerMethodField() )
unreachable_amount = serializers.SerializerMethodField(label=_('Unreachable'))
assets_amount = serializers.SerializerMethodField(label=_('Asset'))
reachable_amount = serializers.SerializerMethodField(label=_('Reachable'))
class Meta: class Meta:
list_serializer_class = AdaptedBulkListSerializer list_serializer_class = AdaptedBulkListSerializer
model = AdminUser model = AdminUser
fields = '__all__' fields = [
'id', 'org_id', 'name', 'username', 'assets_amount',
'reachable_amount', 'unreachable_amount', 'password', 'comment',
'date_created', 'date_updated', 'become', 'become_method',
'become_user', 'created_by',
]
extra_kwargs = {
'date_created': {'label': _('Date created')},
'date_updated': {'label': _('Date updated')},
'become': {'read_only': True}, 'become_method': {'read_only': True},
'become_user': {'read_only': True}, 'created_by': {'read_only': True}
}
def get_field_names(self, declared_fields, info): def get_field_names(self, declared_fields, info):
fields = super().get_field_names(declared_fields, info) fields = super().get_field_names(declared_fields, info)
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
# #
from rest_framework import serializers from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from orgs.mixins import OrgResourceSerializerMixin
from common.mixins import BulkSerializerMixin from common.mixins import BulkSerializerMixin
from common.serializers import AdaptedBulkListSerializer from common.serializers import AdaptedBulkListSerializer
from ..models import Asset from ..models import Asset
...@@ -13,15 +16,35 @@ __all__ = [ ...@@ -13,15 +16,35 @@ __all__ = [
] ]
class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer): class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer, OrgResourceSerializerMixin):
""" """
资产的数据结构 资产的数据结构
""" """
class Meta: class Meta:
model = Asset model = Asset
list_serializer_class = AdaptedBulkListSerializer list_serializer_class = AdaptedBulkListSerializer
fields = '__all__' # validators = [] # 解决批量导入时unique_together字段校验失败
validators = [] fields = [
'id', 'org_id', 'org_name', 'ip', 'hostname', 'protocol', 'port',
'platform', 'is_active', 'public_ip', 'domain', 'admin_user',
'nodes', 'labels', 'number', 'vendor', 'model', 'sn',
'cpu_model', 'cpu_count', 'cpu_cores', 'cpu_vcpus', 'memory',
'disk_total', 'disk_info', 'os', 'os_version', 'os_arch',
'hostname_raw', 'comment', 'created_by', 'date_created',
'hardware_info', 'connectivity'
]
read_only_fields = (
'number', 'vendor', 'model', 'sn', 'cpu_model', 'cpu_count',
'cpu_cores', 'cpu_vcpus', 'memory', 'disk_total', 'disk_info',
'os', 'os_version', 'os_arch', 'hostname_raw',
'created_by', 'date_created',
)
extra_kwargs = {
'hardware_info': {'label': _('Hardware info')},
'connectivity': {'label': _('Connectivity')},
'org_name': {'label': _('Org name')}
}
@classmethod @classmethod
def setup_eager_loading(cls, queryset): def setup_eager_loading(cls, queryset):
......
from rest_framework import serializers from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from common.serializers import AdaptedBulkListSerializer from common.serializers import AdaptedBulkListSerializer
from ..models import SystemUser, Asset from ..models import SystemUser, Asset
...@@ -10,16 +12,36 @@ class SystemUserSerializer(serializers.ModelSerializer): ...@@ -10,16 +12,36 @@ class SystemUserSerializer(serializers.ModelSerializer):
""" """
系统用户 系统用户
""" """
unreachable_amount = serializers.SerializerMethodField() password = serializers.CharField(
reachable_amount = serializers.SerializerMethodField() required=False, write_only=True, label=_('Password')
unreachable_assets = serializers.SerializerMethodField() )
reachable_assets = serializers.SerializerMethodField() unreachable_amount = serializers.SerializerMethodField(
assets_amount = serializers.SerializerMethodField() label=_('Unreachable')
)
unreachable_assets = serializers.SerializerMethodField(
label=_('Unreachable assets')
)
reachable_assets = serializers.SerializerMethodField(
label=_('Reachable assets')
)
reachable_amount = serializers.SerializerMethodField(label=_('Reachable'))
assets_amount = serializers.SerializerMethodField(label=_('Asset'))
class Meta: class Meta:
model = SystemUser model = SystemUser
exclude = ('_password', '_private_key', '_public_key')
list_serializer_class = AdaptedBulkListSerializer list_serializer_class = AdaptedBulkListSerializer
fields = [
'id', 'org_id', 'name', 'username', 'login_mode',
'login_mode_display', 'priority', 'protocol', 'auto_push',
'password', 'assets_amount', 'reachable_amount', 'reachable_assets',
'unreachable_amount', 'unreachable_assets', 'cmd_filters', 'sudo',
'shell', 'comment', 'nodes', 'assets'
]
extra_kwargs = {
'login_mode_display': {'label': _('Login mode display')},
'created_by': {'read_only': True}, 'nodes': {'read_only': True},
'assets': {'read_only': True}
}
def get_field_names(self, declared_fields, info): def get_field_names(self, declared_fields, info):
fields = super(SystemUserSerializer, self).get_field_names(declared_fields, info) fields = super(SystemUserSerializer, self).get_field_names(declared_fields, info)
......
{% extends '_import_modal.html' %}
{% load i18n %}
{% block modal_title%}{% trans "Import admin user" %}{% endblock %}
{% block import_modal_download_template_url %}{% url "api-assets:admin-user-list" %}{% endblock %}
{% extends '_update_modal.html' %}
{% load i18n %}
{% block modal_title%}{% trans "Update admin user" %}{% endblock %}
\ No newline at end of file
{% extends '_modal.html' %} {% extends '_import_modal.html' %}
{% load i18n %} {% load i18n %}
{% block modal_id %}asset_import_modal{% endblock %}
{% block modal_title%}{% trans "Import asset" %}{% endblock %} {% block modal_title%}{% trans "Import assets" %}{% endblock %}
{% block modal_body %}
<form method="post" action="{% url 'assets:asset-import' %}" id="fm_asset_import" enctype="multipart/form-data"> {% block import_modal_download_template_url %}{% url "api-assets:asset-list" %}{% endblock %}
{% csrf_token %}
<div class="form-group">
<label class="control-label" for="id_assets">{% trans "Template" %}</label>
<a href="{% url 'assets:asset-export' %}" style="display: block">{% trans 'Download' %}</a>
</div>
<div class="form-group">
<label class="control-label" for="id_users">{% trans "Asset csv file" %}</label>
<input id="id_assets" type="file" name="file" />
<span class="help-block red-fonts">
{% trans 'If set id, will use this id update asset existed' %}
</span>
</div>
</form>
<p>
<p class="text-success" id="id_created"></p>
<p id="id_created_detail"></p>
<p class="text-warning" id="id_updated"></p>
<p id="id_updated_detail"></p>
<p class="text-danger" id="id_failed"></p>
<p id="id_failed_detail"></p>
</p>
{% endblock %}
{% block modal_confirm_id %}btn_asset_import{% endblock %}
{% extends '_update_modal.html' %}
{% load i18n %}
{% block modal_title%}{% trans "Update assets" %}{% endblock %}
{% extends '_import_modal.html' %}
{% load i18n %}
{% block modal_title%}{% trans "Import system user" %}{% endblock %}
{% block import_modal_download_template_url %}{% url "api-assets:system-user-list" %}{% endblock %}
{% extends '_update_modal.html' %}
{% load i18n %}
{% block modal_title%}{% trans "Update system user" %}{% endblock %}
\ No newline at end of file
{% extends '_base_list.html' %} {% extends '_base_list.html' %}
{% load i18n static %} {% load i18n static %}
{% block table_search %}
{% endblock %}
{% block help_message %} {% block help_message %}
<div class="alert alert-info help-message"> <div class="alert alert-info help-message">
{# 管理用户是资产(被控服务器)上的root,或拥有 NOPASSWD: ALL sudo权限的用户,Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等。#} {# 管理用户是资产(被控服务器)上的root,或拥有 NOPASSWD: ALL sudo权限的用户,Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等。#}
...@@ -12,6 +9,30 @@ ...@@ -12,6 +9,30 @@
{% trans 'You can set any one for Windows or other hardware.' %} {% trans 'You can set any one for Windows or other hardware.' %}
</div> </div>
{% endblock %} {% endblock %}
{% block table_search %}
<div class="" style="float: right">
<div class=" btn-group">
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">CSV <span class="caret"></span></button>
<ul class="dropdown-menu">
<li>
<a class=" btn_export" tabindex="0">
<span>{% trans "Export" %}</span>
</a>
</li>
<li>
<a class=" btn_import" data-toggle="modal" data-target="#import_modal" tabindex="0">
<span>{% trans "Import" %}</span>
</a>
</li>
<li>
<a class=" btn_update" data-toggle="modal" data-target="#update_modal" tabindex="0">
<span>{% trans "Update" %}</span>
</a>
</li>
</ul>
</div>
</div>
{% endblock %}
{% block table_container %} {% block table_container %}
<div class="uc pull-left m-r-5"> <div class="uc pull-left m-r-5">
...@@ -36,6 +57,8 @@ ...@@ -36,6 +57,8 @@
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
{% include 'assets/_admin_user_import_modal.html' %}
{% include 'assets/_admin_user_update_modal.html' %}
{% endblock %} {% endblock %}
{% block content_bottom_left %}{% endblock %} {% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
...@@ -107,6 +130,82 @@ $(document).ready(function(){ ...@@ -107,6 +130,82 @@ $(document).ready(function(){
$data_table.ajax.reload(); $data_table.ajax.reload();
}, 3000); }, 3000);
}); })
.on('click', '.btn_export', function(){
var data_table = $('#admin_user_list_table').DataTable();
var rows = data_table.rows('.selected').data();
var admin_users = [];
$.each(rows, function (index, obj) {
admin_users.push(obj.id)
});
var data = {
'resources': admin_users
};
var search = $("input[type='search']").val();
var props = {
method: "POST",
body: JSON.stringify(data),
success_url: "{% url 'api-assets:admin-user-list' %}",
format: "csv",
params: {
search: search
}
};
APIExportData(props);
}).on('click', '#btn_import_confirm',function () {
var url = "{% url 'api-assets:admin-user-list' %}";
var file = document.getElementById('id_file').files[0];
if(!file){
toastr.error("{% trans "Please select file" %}");
return
}
var data_table = $('#admin_user_list_table').DataTable();
APIImportData({
url: url,
method: "POST",
body: file,
data_table: data_table
});
})
.on('click', '#download_update_template', function () {
var $data_table = $('#admin_user_list_table').DataTable();
var rows = $data_table.rows('.selected').data();
var admin_users = [];
$.each(rows, function (index, obj) {
admin_users.push(obj.id)
});
var data = {
'resources': admin_users
};
var search = $("input[type='search']").val();
var props = {
method: "POST",
body: JSON.stringify(data),
success_url: "{% url 'api-assets:admin-user-list' %}?format=csv&template=update",
format: 'csv',
params: {
search: search
}
};
APIExportData(props);
})
.on('click', '#btn_update_confirm', function () {
var file = document.getElementById('update_file').files[0];
if(!file){
toastr.error("{% trans "Please select file" %}");
return
}
var url = "{% url 'api-assets:admin-user-list' %}";
var data_table = $('#admin_user_list_table').DataTable();
APIImportData({
url: url,
method: "PUT",
body: file,
data_table: data_table
});
})
</script> </script>
{% endblock %} {% endblock %}
...@@ -67,14 +67,26 @@ ...@@ -67,14 +67,26 @@
</div> </div>
<div class="mail-box-header"> <div class="mail-box-header">
<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="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="" style="float: right">
<div class="dt-buttons btn-group"> <div class=" btn-group">
<a class="btn btn-default btn_import" data-toggle="modal" data-target="#asset_import_modal" tabindex="0"> <button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">CSV <span class="caret"></span></button>
<span>{% trans "Import" %}</span> <ul class="dropdown-menu">
</a> <li>
<a class="btn btn-default btn_export" tabindex="0"> <a class=" btn_export" tabindex="0">
<span>{% trans "Export" %}</span> <span>{% trans "Export" %}</span>
</a> </a>
</li>
<li>
<a class=" btn_import" data-toggle="modal" data-target="#import_modal" tabindex="0">
<span>{% trans "Import" %}</span>
</a>
</li>
<li>
<a class=" btn_update" data-toggle="modal" data-target="#update_modal" tabindex="0">
<span>{% trans "Update" %}</span>
</a>
</li>
</ul>
</div> </div>
</div> </div>
<div class="btn-group" style="float: right"> <div class="btn-group" style="float: right">
...@@ -140,7 +152,7 @@ ...@@ -140,7 +152,7 @@
{# <li id="fresh_tree" class="btn-refresh-tree" tabindex="-1"><a><i class="fa fa-refresh"></i> {% trans 'Refresh' %}</a></li>#} {# <li id="fresh_tree" class="btn-refresh-tree" tabindex="-1"><a><i class="fa fa-refresh"></i> {% trans 'Refresh' %}</a></li>#}
</ul> </ul>
</div> </div>
{% include 'assets/_asset_update_modal.html' %}
{% include 'assets/_asset_import_modal.html' %} {% include 'assets/_asset_import_modal.html' %}
{% include 'assets/_asset_list_modal.html' %} {% include 'assets/_asset_list_modal.html' %}
{% endblock %} {% endblock %}
...@@ -464,42 +476,85 @@ $(document).ready(function(){ ...@@ -464,42 +476,85 @@ $(document).ready(function(){
$.each(rows, function (index, obj) { $.each(rows, function (index, obj) {
assets.push(obj.id) assets.push(obj.id)
}); });
$.ajax({
url: "{% url "assets:asset-export" %}", var data = {
method: 'POST', 'resources': assets
data: JSON.stringify({assets_id: assets, node_id: current_node_id}), };
dataType: "json", var search = $("input[type='search']").val();
success: function (data, textStatus) { var props = {
window.open(data.redirect) method: "POST",
}, body: JSON.stringify(data),
error: function () { success_url: "{% url 'api-assets:asset-list' %}",
toastr.error('Export failed'); format: 'csv',
params: {
search: search,
node_id: current_node_id || ''
} }
}) };
APIExportData(props);
}) })
.on('click', '#btn_asset_import', function () { .on('click', '#btn_import_confirm', function () {
var $form = $('#fm_asset_import'); var file = document.getElementById('id_file').files[0];
var action = $form.attr("action"); if(!file){
toastr.error("{% trans "Please select file" %}");
return
}
var url = "{% url 'api-assets:asset-list' %}";
if (current_node_id){ if (current_node_id){
action = setUrlParam(action, 'node_id', current_node_id); url = setUrlParam(url, 'node_id', current_node_id);
$form.attr("action", action)
} }
$form.find('.help-block').remove(); var data_table = $('#asset_list_table').DataTable();
function success (data) {
if (data.valid === false) { APIImportData({
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_assets')); url: url,
} else { method: "POST",
$('#id_created').html(data.created_info); body: file,
$('#id_created_detail').html(data.created.join(', ')); data_table: data_table
$('#id_updated').html(data.updated_info); });
$('#id_updated_detail').html(data.updated.join(', ')); })
$('#id_failed').html(data.failed_info); .on('click', '#download_update_template', function () {
$('#id_failed_detail').html(data.failed.join(', ')); var $data_table = $('#asset_list_table').DataTable();
var $data_table = $('#asset_list_table').DataTable(); var rows = $data_table.rows('.selected').data();
$data_table.ajax.reload();
var assets = [];
$.each(rows, function (index, obj) {
assets.push(obj.id)
});
var data = {
'resources': assets
};
var search = $("input[type='search']").val();
var props = {
method: "POST",
body: JSON.stringify(data),
success_url: "{% url 'api-assets:asset-list' %}?format=csv&template=update",
format: 'csv',
params: {
search: search,
node_id: current_node_id || ''
} }
};
APIExportData(props);
})
.on('click', '#btn_update_confirm', function () {
var file = document.getElementById('update_file').files[0];
if(!file){
toastr.error("{% trans "Please select file" %}");
return
}
var url = "{% url 'api-assets:asset-list' %}";
if (current_node_id){
url = setUrlParam(url, 'node_id', current_node_id);
} }
$form.ajaxSubmit({success: success}); var data_table = $('#asset_list_table').DataTable();
APIImportData({
url: url,
method: "PUT",
body: file,
data_table: data_table
});
}) })
.on('click', '.btn-create-asset', function () { .on('click', '.btn-create-asset', function () {
var url = "{% url 'assets:asset-create' %}"; var url = "{% url 'assets:asset-create' %}";
...@@ -593,6 +648,12 @@ $(document).ready(function(){ ...@@ -593,6 +648,12 @@ $(document).ready(function(){
return false; return false;
} }
var the_url = "{% url 'api-assets:asset-list' %}"; var the_url = "{% url 'api-assets:asset-list' %}";
var data = {
'resources': id_list
};
function refreshTag() {
$('#asset_list_table').DataTable().ajax.reload();
}
function doDeactive() { function doDeactive() {
var data = []; var data = [];
...@@ -601,7 +662,8 @@ $(document).ready(function(){ ...@@ -601,7 +662,8 @@ $(document).ready(function(){
data.push(obj); data.push(obj);
}); });
function success() { function success() {
asset_table.ajax.reload() setTimeout( function () {
window.location.reload();}, 500);
} }
APIUpdateAttr({ APIUpdateAttr({
url: the_url, url: the_url,
...@@ -617,7 +679,8 @@ $(document).ready(function(){ ...@@ -617,7 +679,8 @@ $(document).ready(function(){
data.push(obj); data.push(obj);
}); });
function success() { function success() {
asset_table.ajax.reload() setTimeout( function () {
window.location.reload();}, 300);
} }
APIUpdateAttr({ APIUpdateAttr({
url: the_url, url: the_url,
...@@ -636,68 +699,72 @@ $(document).ready(function(){ ...@@ -636,68 +699,72 @@ $(document).ready(function(){
confirmButtonColor: "#DD6B55", confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}", confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false closeOnConfirm: false
}, function() { },function () {
var success = function() { function success(data) {
url = setUrlParam(the_url, 'spm', data.spm);
APIUpdateAttr({
url:url,
method:'DELETE',
success:refreshTag,
flash_message:false,
});
var msg = "{% trans 'Asset Deleted.' %}"; var msg = "{% trans 'Asset Deleted.' %}";
swal("{% trans 'Asset Delete' %}", msg, "success"); swal("{% trans 'Asset Delete' %}", msg, "success");
$('#asset_list_table').DataTable().ajax.reload(); }
}; function fail() {
var fail = function() {
var msg = "{% trans 'Asset Deleting failed.' %}"; var msg = "{% trans 'Asset Deleting failed.' %}";
swal("{% trans 'Asset Delete' %}", msg, "error"); swal("{% trans 'Asset Delete' %}", msg, "error");
}; }
var url_delete = the_url + '?id__in=' + JSON.stringify(id_list);
APIUpdateAttr({ APIUpdateAttr({
url: url_delete, url: "{% url 'api-common:resources-cache' %}",
method: 'DELETE', method:'POST',
success: success, body:JSON.stringify(data),
error: fail success:success,
}); error:fail
$data_table.ajax.reload(); })
jumpserver.checked = false; })
});
} }
function doUpdate() { function doUpdate() {
var data = { function fail(data) {
'assets_id':id_list toastr.error(JSON.parse(data))
};
function error(data) {
toastr.error(JSON.parse(data).error)
} }
function success(data) { function success(data) {
location.href = data.url; var url = "{% url 'assets:asset-bulk-update' %}";
location.href= setUrlParam(url, 'spm', data.spm);
} }
APIUpdateAttr({ APIUpdateAttr({
'url': "{% url 'api-assets:asset-bulk-update-select' %}", url: "{% url 'api-common:resources-cache' %}",
'method': 'POST', method:'POST',
'body': JSON.stringify(data), body:JSON.stringify(data),
'flash_message': false, flash_message:false,
'success': success, success:success,
'error': error, error:fail
}) })
} }
function doRemove() { function doRemove() {
var nodes = zTree.getSelectedNodes(); var nodes = zTree.getSelectedNodes();
if (!current_node_id) { if (!current_node_id) {
return return
} }
var data = { var data = {
'assets': id_list 'assets': id_list
}; };
var success = function () { var success = function () {
asset_table.ajax.reload() asset_table.ajax.reload()
}; };
APIUpdateAttr({ APIUpdateAttr({
'url': '/api/assets/v1/nodes/' + current_node_id + '/assets/remove/', 'url': '/api/assets/v1/nodes/' + current_node_id + '/assets/remove/',
'method': 'PUT', 'method': 'PUT',
'body': JSON.stringify(data), 'body': JSON.stringify(data),
'success': success 'success': success
}) })
} }
switch(action) { switch(action) {
case 'deactive': case 'deactive':
doDeactive(); doDeactive();
......
...@@ -14,6 +14,28 @@ ...@@ -14,6 +14,28 @@
{% endblock %} {% endblock %}
{% block table_search %} {% block table_search %}
<div class="" style="float: right">
<div class=" btn-group">
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">CSV <span class="caret"></span></button>
<ul class="dropdown-menu">
<li>
<a class=" btn_export" tabindex="0">
<span>{% trans "Export" %}</span>
</a>
</li>
<li>
<a class=" btn_import" data-toggle="modal" data-target="#import_modal" tabindex="0">
<span>{% trans "Import" %}</span>
</a>
</li>
<li>
<a class=" btn_update" data-toggle="modal" data-target="#update_modal" tabindex="0">
<span>{% trans "Update" %}</span>
</a>
</li>
</ul>
</div>
</div>
{% endblock %} {% endblock %}
{% block table_container %} {% block table_container %}
...@@ -41,6 +63,8 @@ ...@@ -41,6 +63,8 @@
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
{% include 'assets/_system_user_import_modal.html' %}
{% include 'assets/_system_user_update_modal.html' %}
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
...@@ -173,6 +197,81 @@ $(document).ready(function(){ ...@@ -173,6 +197,81 @@ $(document).ready(function(){
break; break;
} }
}) })
.on('click', '.btn_export', function () {
var data_table = $('#system_user_list_table').DataTable();
var rows = data_table.rows('.selected').data();
var system_users = [];
$.each(rows, function (index, obj) {
system_users.push(obj.id)
});
var data = {
'resources': system_users
};
var search = $("input[type='search']").val();
var props = {
method: "POST",
body: JSON.stringify(data),
success_url: "{% url 'api-assets:system-user-list' %}",
format: "csv",
params:{
search:search
}
};
APIExportData(props);
})
.on('click', '#btn_import_confirm', function () {
var url = "{% url 'api-assets:system-user-list' %}";
var file = document.getElementById('id_file').files[0];
if(!file){
toastr.error("{% trans 'Please select file' %}");
return
}
var data_table = $('#system_user_list_table').DataTable();
APIImportData({
url: url,
method: "POST",
body: file,
data_table: data_table
});
})
.on('click', '#download_update_template', function () {
var data_table = $('#system_user_list_table').DataTable();
var rows = data_table.rows('.selected').data();
var system_users = [];
$.each(rows, function (index, obj) {
system_users.push(obj.id)
});
var data = {
'resources': system_users
};
var search = $("input[type='search']").val();
var props = {
method: "POST",
body: JSON.stringify(data),
success_url: "{% url 'api-assets:system-user-list' %}?format=csv&template=update",
format: "csv",
params:{
search:search
}
};
APIExportData(props);
})
.on('click', '#btn_update_confirm', function () {
var file = document.getElementById('update_file').files[0];
if(!file){
toastr.error("{% trans "Please select file" %}");
return
}
var url = "{% url 'api-assets:system-user-list' %}";
var data_table = $('#system_user_list_table').DataTable();
APIImportData({
url: url,
method: "PUT",
body: file,
data_table: data_table
});
})
</script> </script>
{% endblock %} {% endblock %}
......
...@@ -27,7 +27,9 @@ from django.contrib.messages.views import SuccessMessageMixin ...@@ -27,7 +27,9 @@ from django.contrib.messages.views import SuccessMessageMixin
from common.mixins import JSONResponseMixin from common.mixins import JSONResponseMixin
from common.utils import get_object_or_none, get_logger from common.utils import get_object_or_none, get_logger
from common.permissions import AdminUserRequiredMixin from common.permissions import AdminUserRequiredMixin
from common.const import create_success_msg, update_success_msg from common.const import (
create_success_msg, update_success_msg, KEY_CACHE_RESOURCES_ID
)
from ..const import CACHE_KEY_ASSET_BULK_UPDATE_ID_PREFIX from ..const import CACHE_KEY_ASSET_BULK_UPDATE_ID_PREFIX
from orgs.utils import current_org from orgs.utils import current_org
from .. import forms from .. import forms
...@@ -122,7 +124,7 @@ class AssetBulkUpdateView(AdminUserRequiredMixin, ListView): ...@@ -122,7 +124,7 @@ class AssetBulkUpdateView(AdminUserRequiredMixin, ListView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
spm = request.GET.get('spm', '') spm = request.GET.get('spm', '')
assets_id = cache.get(CACHE_KEY_ASSET_BULK_UPDATE_ID_PREFIX.format(spm)) assets_id = cache.get(KEY_CACHE_RESOURCES_ID.format(spm))
if kwargs.get('form'): if kwargs.get('form'):
self.form = kwargs['form'] self.form = kwargs['form']
elif assets_id: elif assets_id:
......
...@@ -3,10 +3,18 @@ ...@@ -3,10 +3,18 @@
import os import os
import uuid import uuid
from rest_framework.views import Response
from rest_framework import generics, serializers
from django.core.cache import cache from django.core.cache import cache
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import generics, serializers
from .const import KEY_CACHE_RESOURCES_ID
__all__ = [
'LogTailApi', 'ResourcesIDCacheApi',
]
class OutputSerializer(serializers.Serializer): class OutputSerializer(serializers.Serializer):
output = serializers.CharField() output = serializers.CharField()
...@@ -68,3 +76,14 @@ class LogTailApi(generics.RetrieveAPIView): ...@@ -68,3 +76,14 @@ class LogTailApi(generics.RetrieveAPIView):
data, end, new_mark = self.read_from_file() data, end, new_mark = self.read_from_file()
return Response({"data": data, 'end': end, 'mark': new_mark}) return Response({"data": data, 'end': end, 'mark': new_mark})
class ResourcesIDCacheApi(APIView):
def post(self, request, *args, **kwargs):
spm = str(uuid.uuid4())
resources_id = request.data.get('resources')
if resources_id:
cache_key = KEY_CACHE_RESOURCES_ID.format(spm)
cache.set(cache_key, resources_id, 300)
return Response({'spm': spm})
...@@ -7,3 +7,4 @@ create_success_msg = _("%(name)s was created successfully") ...@@ -7,3 +7,4 @@ create_success_msg = _("%(name)s was created successfully")
update_success_msg = _("%(name)s was updated successfully") update_success_msg = _("%(name)s was updated successfully")
FILE_END_GUARD = ">>> Content End <<<" FILE_END_GUARD = ">>> Content End <<<"
celery_task_pre_key = "CELERY_" celery_task_pre_key = "CELERY_"
KEY_CACHE_RESOURCES_ID = "RESOURCES_ID_{}"
...@@ -3,12 +3,15 @@ ...@@ -3,12 +3,15 @@
from django.db import models from django.db import models
from django.http import JsonResponse from django.http import JsonResponse
from django.utils import timezone from django.utils import timezone
from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework.utils import html from rest_framework.utils import html
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from rest_framework.fields import SkipField from rest_framework.fields import SkipField
from .const import KEY_CACHE_RESOURCES_ID
class NoDeleteQuerySet(models.query.QuerySet): class NoDeleteQuerySet(models.query.QuerySet):
...@@ -65,6 +68,27 @@ class IDInFilterMixin(object): ...@@ -65,6 +68,27 @@ class IDInFilterMixin(object):
return queryset return queryset
class IDInCacheFilterMixin(object):
def filter_queryset(self, queryset):
queryset = super(IDInCacheFilterMixin, self).filter_queryset(queryset)
spm = self.request.query_params.get('spm')
cache_key = KEY_CACHE_RESOURCES_ID.format(spm)
resources_id = cache.get(cache_key)
if resources_id and isinstance(resources_id, list):
queryset = queryset.filter(id__in=resources_id)
return queryset
class IDExportFilterMixin(object):
def filter_queryset(self, queryset):
# 下载导入模版
if self.request.query_params.get('template') == 'import':
return []
else:
return super(IDExportFilterMixin, self).filter_queryset(queryset)
class BulkSerializerMixin(object): class BulkSerializerMixin(object):
""" """
Become rest_framework_bulk not support uuid as a primary key Become rest_framework_bulk not support uuid as a primary key
...@@ -131,7 +155,11 @@ class BulkListSerializerMixin(object): ...@@ -131,7 +155,11 @@ class BulkListSerializerMixin(object):
for item in data: for item in data:
try: try:
# prepare child serializer to only handle one instance # prepare child serializer to only handle one instance
self.child.instance = self.instance.get(id=item['id']) if self.instance else None if 'id' in item.keys():
self.child.instance = self.instance.get(id=item['id']) if self.instance else None
if 'pk' in item.keys():
self.child.instance = self.instance.get(id=item['pk']) if self.instance else None
self.child.initial_data = item self.child.initial_data = item
# raw # raw
validated = self.child.run_validation(item) validated = self.child.run_validation(item)
......
from .csv import *
\ No newline at end of file
# ~*~ coding: utf-8 ~*~
#
import json
import unicodecsv
from rest_framework.parsers import BaseParser
from rest_framework.exceptions import ParseError
from ..utils import get_logger
logger = get_logger(__file__)
class JMSCSVParser(BaseParser):
"""
Parses CSV file to serializer data
"""
media_type = 'text/csv'
@staticmethod
def _universal_newlines(stream):
"""
保证在`通用换行模式`下打开文件
"""
for line in stream.splitlines():
yield line
@staticmethod
def _gen_rows(csv_data, charset='utf-8', **kwargs):
csv_reader = unicodecsv.reader(csv_data, encoding=charset, **kwargs)
for row in csv_reader:
if not any(row): # 空行
continue
yield row
@staticmethod
def _get_fields_map(serializer):
fields_map = {}
fields = serializer.get_fields()
fields_map.update({v.label: k for k, v in fields.items()})
fields_map.update({k: k for k, _ in fields.items()})
return fields_map
@staticmethod
def _process_row(row):
"""
构建json数据前的行处理
"""
_row = []
for col in row:
# 列表转换
if isinstance(col, str) and col.find("[") != -1 and col.find("]") != -1:
# 替换中文格式引号
col = col.replace("“", '"').replace("”", '"').\
replace("‘", '"').replace('’', '"').replace("'", '"')
col = json.loads(col)
_row.append(col)
return _row
@staticmethod
def _process_row_data(row_data):
"""
构建json数据后的行数据处理
"""
_row_data = {}
for k, v in row_data.items():
if isinstance(v, list) \
or isinstance(v, str) and k.strip() and v.strip():
_row_data[k] = v
return _row_data
def parse(self, stream, media_type=None, parser_context=None):
parser_context = parser_context or {}
encoding = parser_context.get('encoding', 'utf-8')
try:
serializer = parser_context["view"].get_serializer()
except Exception as e:
logger.debug(e, exc_info=True)
raise ParseError('The resource does not support imports!')
try:
stream_data = stream.read()
binary = self._universal_newlines(stream_data)
rows = self._gen_rows(binary, charset=encoding)
header = next(rows)
fields_map = self._get_fields_map(serializer)
header = [fields_map.get(name, '') for name in header]
data = []
for row in rows:
row = self._process_row(row)
row_data = dict(zip(header, row))
row_data = self._process_row_data(row_data)
data.append(row_data)
return data
except Exception as e:
logger.debug(e, exc_info=True)
raise ParseError('CSV parse error!')
from .csv import *
\ No newline at end of file
# ~*~ coding: utf-8 ~*~
#
import unicodecsv
from six import BytesIO
from rest_framework.renderers import BaseRenderer
from rest_framework.utils import encoders, json
from ..utils import get_logger
logger = get_logger(__file__)
class JMSCSVRender(BaseRenderer):
media_type = 'text/csv'
format = 'csv'
@staticmethod
def _get_header(fields, template):
if template == 'import':
header = [
k for k, v in fields.items()
if not v.read_only and k != 'org_id'
]
elif template == 'update':
header = [k for k, v in fields.items() if not v.read_only]
else:
# template in ['export']
header = [k for k, v in fields.items() if not v.write_only]
return header
@staticmethod
def _gen_table(data, header, labels=None):
labels = labels or {}
yield [labels.get(k, k) for k in header]
for item in data:
row = [item.get(key) for key in header]
yield row
def render(self, data, media_type=None, renderer_context=None):
renderer_context = renderer_context or {}
encoding = renderer_context.get('encoding', 'utf-8')
request = renderer_context['request']
template = request.query_params.get('template', 'export')
view = renderer_context['view']
data = json.loads(json.dumps(data, cls=encoders.JSONEncoder))
if template == 'import':
data = [data[0]] if data else data
try:
serializer = view.get_serializer()
except Exception as e:
logger.debug(e, exc_info=True)
value = 'The resource not support export!'.encode('utf-8')
else:
fields = serializer.get_fields()
header = self._get_header(fields, template)
labels = {k: v.label for k, v in fields.items() if v.label}
table = self._gen_table(data, header, labels)
csv_buffer = BytesIO()
csv_writer = unicodecsv.writer(csv_buffer, encoding=encoding)
for row in table:
csv_writer.writerow(row)
value = csv_buffer.getvalue()
return value
# -*- coding: utf-8 -*-
#
from django.urls import path
from .. import api
app_name = 'common'
urlpatterns = [
path('resources/cache/',
api.ResourcesIDCacheApi.as_view(), name='resources-cache'),
]
...@@ -363,6 +363,16 @@ REST_FRAMEWORK = { ...@@ -363,6 +363,16 @@ REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ( 'DEFAULT_PERMISSION_CLASSES': (
'common.permissions.IsOrgAdmin', 'common.permissions.IsOrgAdmin',
), ),
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
'common.renders.JMSCSVRender',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'common.parsers.JMSCSVParser'
),
'DEFAULT_AUTHENTICATION_CLASSES': ( 'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework.authentication.BasicAuthentication', # 'rest_framework.authentication.BasicAuthentication',
'authentication.backends.api.AccessKeyAuthentication', 'authentication.backends.api.AccessKeyAuthentication',
......
...@@ -20,6 +20,7 @@ api_v1 = [ ...@@ -20,6 +20,7 @@ api_v1 = [
path('orgs/v1/', include('orgs.urls.api_urls', namespace='api-orgs')), path('orgs/v1/', include('orgs.urls.api_urls', namespace='api-orgs')),
path('settings/v1/', include('settings.urls.api_urls', namespace='api-settings')), path('settings/v1/', include('settings.urls.api_urls', namespace='api-settings')),
path('authentication/v1/', include('authentication.urls.api_urls', namespace='api-auth')), path('authentication/v1/', include('authentication.urls.api_urls', namespace='api-auth')),
path('common/v1/', include('common.urls.api_urls', namespace='api-common')),
path('applications/v1/', include('applications.urls.api_urls', namespace='api-applications')), path('applications/v1/', include('applications.urls.api_urls', namespace='api-applications')),
] ]
......
...@@ -621,7 +621,7 @@ msgstr "管理用户" ...@@ -621,7 +621,7 @@ msgstr "管理用户"
#: assets/forms/asset.py:33 assets/forms/asset.py:69 assets/forms/asset.py:109 #: assets/forms/asset.py:33 assets/forms/asset.py:69 assets/forms/asset.py:109
#: assets/templates/assets/asset_create.html:36 #: assets/templates/assets/asset_create.html:36
#: assets/templates/assets/asset_create.html:38 #: assets/templates/assets/asset_create.html:38
#: assets/templates/assets/asset_list.html:81 #: assets/templates/assets/asset_list.html:93
#: assets/templates/assets/asset_update.html:41 #: assets/templates/assets/asset_update.html:41
#: assets/templates/assets/asset_update.html:43 #: assets/templates/assets/asset_update.html:43
#: assets/templates/assets/user_asset_list.html:33 #: assets/templates/assets/user_asset_list.html:33
...@@ -680,6 +680,37 @@ msgstr "如果有多个的互相隔离的网络,设置资产属于的网域, ...@@ -680,6 +680,37 @@ msgstr "如果有多个的互相隔离的网络,设置资产属于的网域,
msgid "Select assets" msgid "Select assets"
msgstr "选择资产" msgstr "选择资产"
#: assets/forms/domain.py:15 assets/forms/label.py:13
#: assets/models/asset.py:279 assets/models/authbook.py:27
#: assets/serializers/admin_user.py:31 assets/serializers/system_user.py:32
#: assets/templates/assets/admin_user_list.html:49
#: assets/templates/assets/domain_detail.html:60
#: assets/templates/assets/domain_list.html:26
#: assets/templates/assets/label_list.html:16
#: assets/templates/assets/system_user_list.html:55 audits/models.py:19
#: audits/templates/audits/ftp_log_list.html:41
#: audits/templates/audits/ftp_log_list.html:71 perms/forms.py:42
#: perms/models.py:50
#: perms/templates/perms/asset_permission_create_update.html:45
#: perms/templates/perms/asset_permission_list.html:56
#: perms/templates/perms/asset_permission_list.html:125
#: terminal/backends/command/models.py:13 terminal/models.py:155
#: terminal/templates/terminal/command_list.html:40
#: terminal/templates/terminal/command_list.html:73
#: terminal/templates/terminal/session_list.html:41
#: terminal/templates/terminal/session_list.html:72
#: xpack/plugins/change_auth_plan/forms.py:114
#: xpack/plugins/change_auth_plan/models.py:409
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:46
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:54
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:13
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:14
#: xpack/plugins/cloud/models.py:187
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:63
#: xpack/plugins/orgs/templates/orgs/org_list.html:16
msgid "Asset"
msgstr "资产"
#: assets/forms/domain.py:51 #: assets/forms/domain.py:51
msgid "Password should not contain special characters" msgid "Password should not contain special characters"
msgstr "不能包含特殊字符" msgstr "不能包含特殊字符"
...@@ -688,16 +719,65 @@ msgstr "不能包含特殊字符" ...@@ -688,16 +719,65 @@ msgstr "不能包含特殊字符"
msgid "SSH gateway support proxy SSH,RDP,VNC" msgid "SSH gateway support proxy SSH,RDP,VNC"
msgstr "SSH网关,支持代理SSH,RDP和VNC" msgstr "SSH网关,支持代理SSH,RDP和VNC"
#: assets/forms/domain.py:73 assets/forms/user.py:84 assets/forms/user.py:146
#: assets/models/base.py:26 assets/models/cluster.py:18
#: assets/models/cmd_filter.py:20 assets/models/domain.py:20
#: assets/models/group.py:20 assets/models/label.py:18
#: assets/templates/assets/admin_user_detail.html:56
#: assets/templates/assets/admin_user_list.html:47
#: assets/templates/assets/cmd_filter_detail.html:61
#: assets/templates/assets/cmd_filter_list.html:24
#: assets/templates/assets/domain_detail.html:56
#: assets/templates/assets/domain_gateway_list.html:67
#: assets/templates/assets/domain_list.html:25
#: assets/templates/assets/label_list.html:14
#: assets/templates/assets/system_user_detail.html:58
#: assets/templates/assets/system_user_list.html:51 ops/models/adhoc.py:37
#: ops/templates/ops/task_detail.html:60 ops/templates/ops/task_list.html:27
#: orgs/models.py:12 perms/models.py:17 perms/models.py:47
#: perms/templates/perms/asset_permission_detail.html:62
#: perms/templates/perms/asset_permission_list.html:53
#: perms/templates/perms/asset_permission_list.html:72
#: perms/templates/perms/asset_permission_user.html:54 settings/models.py:29
#: settings/templates/settings/_ldap_list_users_modal.html:38
#: settings/templates/settings/command_storage_create.html:41
#: settings/templates/settings/replay_storage_create.html:44
#: settings/templates/settings/terminal_setting.html:80
#: settings/templates/settings/terminal_setting.html:102 terminal/models.py:22
#: terminal/models.py:241 terminal/templates/terminal/terminal_detail.html:43
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
#: users/models/user.py:61 users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_detail.html:63
#: users/templates/users/user_group_detail.html:55
#: users/templates/users/user_group_list.html:35
#: users/templates/users/user_list.html:35
#: users/templates/users/user_profile.html:51
#: users/templates/users/user_pubkey_update.html:53
#: xpack/plugins/change_auth_plan/forms.py:97
#: xpack/plugins/change_auth_plan/models.py:58
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:61
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:12
#: xpack/plugins/cloud/models.py:49 xpack/plugins/cloud/models.py:119
#: xpack/plugins/cloud/templates/cloud/account_detail.html:50
#: xpack/plugins/cloud/templates/cloud/account_list.html:12
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:53
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:12
#: xpack/plugins/orgs/templates/orgs/org_detail.html:52
#: xpack/plugins/orgs/templates/orgs/org_list.html:12
msgid "Name"
msgstr "名称"
#: assets/forms/domain.py:74 assets/forms/user.py:85 assets/forms/user.py:147 #: assets/forms/domain.py:74 assets/forms/user.py:85 assets/forms/user.py:147
#: assets/models/base.py:27 #: assets/models/base.py:27
#: assets/templates/assets/_asset_user_auth_modal.html:15 #: assets/templates/assets/_asset_user_auth_modal.html:15
#: assets/templates/assets/_asset_user_view_auth_modal.html:31 #: assets/templates/assets/_asset_user_view_auth_modal.html:31
#: assets/templates/assets/admin_user_detail.html:60 #: assets/templates/assets/admin_user_detail.html:60
#: assets/templates/assets/admin_user_list.html:27
#: assets/templates/assets/admin_user_list.html:48
#: assets/templates/assets/asset_asset_user_list.html:44 #: assets/templates/assets/asset_asset_user_list.html:44
#: assets/templates/assets/domain_gateway_list.html:71 #: assets/templates/assets/domain_gateway_list.html:71
#: assets/templates/assets/system_user_detail.html:62 #: assets/templates/assets/system_user_detail.html:62
#: assets/templates/assets/system_user_list.html:30 audits/models.py:94 #: assets/templates/assets/system_user_list.html:52 audits/models.py:94
#: audits/templates/audits/login_log_list.html:51 authentication/forms.py:11 #: audits/templates/audits/login_log_list.html:51 authentication/forms.py:11
#: authentication/templates/authentication/login.html:64 #: authentication/templates/authentication/login.html:64
#: authentication/templates/authentication/new_login.html:90 #: authentication/templates/authentication/new_login.html:90
...@@ -707,7 +787,7 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC" ...@@ -707,7 +787,7 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC"
#: settings/templates/settings/_ldap_list_users_modal.html:37 users/forms.py:13 #: settings/templates/settings/_ldap_list_users_modal.html:37 users/forms.py:13
#: users/models/user.py:59 users/templates/users/_select_user_modal.html:14 #: users/models/user.py:59 users/templates/users/_select_user_modal.html:14
#: users/templates/users/user_detail.html:67 #: users/templates/users/user_detail.html:67
#: users/templates/users/user_list.html:24 #: users/templates/users/user_list.html:36
#: users/templates/users/user_profile.html:47 #: users/templates/users/user_profile.html:47
#: xpack/plugins/change_auth_plan/forms.py:99 #: xpack/plugins/change_auth_plan/forms.py:99
#: xpack/plugins/change_auth_plan/models.py:60 #: xpack/plugins/change_auth_plan/models.py:60
...@@ -724,7 +804,8 @@ msgid "Password or private key passphrase" ...@@ -724,7 +804,8 @@ msgid "Password or private key passphrase"
msgstr "密码或密钥密码" msgstr "密码或密钥密码"
#: assets/forms/user.py:26 assets/models/base.py:28 #: assets/forms/user.py:26 assets/models/base.py:28
#: assets/serializers/asset_user.py:19 #: assets/serializers/admin_user.py:20 assets/serializers/asset_user.py:19
#: assets/serializers/system_user.py:16
#: assets/templates/assets/_asset_user_auth_modal.html:21 #: assets/templates/assets/_asset_user_auth_modal.html:21
#: assets/templates/assets/_asset_user_view_auth_modal.html:37 #: assets/templates/assets/_asset_user_view_auth_modal.html:37
#: authentication/forms.py:13 #: authentication/forms.py:13
...@@ -792,7 +873,7 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig" ...@@ -792,7 +873,7 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig"
#: assets/templates/assets/_asset_list_modal.html:46 #: assets/templates/assets/_asset_list_modal.html:46
#: assets/templates/assets/admin_user_assets.html:49 #: assets/templates/assets/admin_user_assets.html:49
#: assets/templates/assets/asset_detail.html:64 #: assets/templates/assets/asset_detail.html:64
#: assets/templates/assets/asset_list.html:93 #: assets/templates/assets/asset_list.html:105
#: assets/templates/assets/domain_gateway_list.html:68 #: assets/templates/assets/domain_gateway_list.html:68
#: assets/templates/assets/system_user_asset.html:51 #: assets/templates/assets/system_user_asset.html:51
#: assets/templates/assets/user_asset_list.html:45 #: assets/templates/assets/user_asset_list.html:45
...@@ -810,7 +891,7 @@ msgstr "IP" ...@@ -810,7 +891,7 @@ msgstr "IP"
#: assets/templates/assets/_asset_user_view_auth_modal.html:25 #: assets/templates/assets/_asset_user_view_auth_modal.html:25
#: assets/templates/assets/admin_user_assets.html:48 #: assets/templates/assets/admin_user_assets.html:48
#: assets/templates/assets/asset_detail.html:60 #: assets/templates/assets/asset_detail.html:60
#: assets/templates/assets/asset_list.html:92 #: assets/templates/assets/asset_list.html:104
#: assets/templates/assets/system_user_asset.html:50 #: assets/templates/assets/system_user_asset.html:50
#: assets/templates/assets/user_asset_list.html:44 #: assets/templates/assets/user_asset_list.html:44
#: assets/templates/assets/user_asset_list.html:162 #: assets/templates/assets/user_asset_list.html:162
...@@ -826,7 +907,7 @@ msgstr "主机名" ...@@ -826,7 +907,7 @@ msgstr "主机名"
#: assets/models/user.py:136 assets/templates/assets/asset_detail.html:76 #: assets/models/user.py:136 assets/templates/assets/asset_detail.html:76
#: assets/templates/assets/domain_gateway_list.html:70 #: assets/templates/assets/domain_gateway_list.html:70
#: assets/templates/assets/system_user_detail.html:70 #: assets/templates/assets/system_user_detail.html:70
#: assets/templates/assets/system_user_list.html:31 #: assets/templates/assets/system_user_list.html:53
#: assets/templates/assets/user_asset_list.html:165 #: assets/templates/assets/user_asset_list.html:165
#: terminal/templates/terminal/session_list.html:75 #: terminal/templates/terminal/session_list.html:75
msgid "Protocol" msgid "Protocol"
...@@ -926,19 +1007,96 @@ msgstr "主机名原始" ...@@ -926,19 +1007,96 @@ msgstr "主机名原始"
msgid "Labels" msgid "Labels"
msgstr "标签管理" msgstr "标签管理"
#: assets/models/asset.py:109 assets/models/base.py:34
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:25
#: assets/models/cmd_filter.py:58 assets/models/group.py:21
#: assets/templates/assets/admin_user_detail.html:68
#: assets/templates/assets/asset_detail.html:128
#: assets/templates/assets/cmd_filter_detail.html:77
#: assets/templates/assets/domain_detail.html:72
#: assets/templates/assets/system_user_detail.html:100
#: ops/templates/ops/adhoc_detail.html:86 orgs/models.py:15 perms/models.py:57
#: perms/models.py:110 perms/templates/perms/asset_permission_detail.html:98
#: users/models/user.py:102 users/serializers/v1.py:69
#: users/templates/users/user_detail.html:111
#: xpack/plugins/change_auth_plan/models.py:103
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113
#: xpack/plugins/cloud/models.py:55 xpack/plugins/cloud/models.py:127
msgid "Created by"
msgstr "创建者"
#: assets/models/asset.py:110 assets/models/cluster.py:26
#: assets/models/domain.py:23 assets/models/group.py:22
#: assets/models/label.py:25 assets/serializers/admin_user.py:23
#: assets/templates/assets/admin_user_detail.html:64
#: assets/templates/assets/cmd_filter_detail.html:69
#: assets/templates/assets/domain_detail.html:68
#: assets/templates/assets/system_user_detail.html:96
#: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:64
#: orgs/models.py:16 perms/models.py:58 perms/models.py:111
#: perms/templates/perms/asset_permission_detail.html:94
#: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:17
#: users/templates/users/user_group_detail.html:63
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:105
#: xpack/plugins/cloud/models.py:56 xpack/plugins/cloud/models.py:128
#: xpack/plugins/cloud/templates/cloud/account_detail.html:66
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:77
#: xpack/plugins/orgs/templates/orgs/org_detail.html:60
msgid "Date created"
msgstr "创建日期"
#: assets/models/asset.py:111 assets/models/base.py:31
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:22
#: assets/models/cmd_filter.py:55 assets/models/domain.py:21
#: assets/models/domain.py:53 assets/models/group.py:23
#: assets/models/label.py:23 assets/templates/assets/admin_user_detail.html:72
#: assets/templates/assets/admin_user_list.html:53
#: assets/templates/assets/asset_detail.html:136
#: assets/templates/assets/cmd_filter_detail.html:65
#: assets/templates/assets/cmd_filter_list.html:27
#: assets/templates/assets/cmd_filter_rule_list.html:62
#: assets/templates/assets/domain_detail.html:76
#: assets/templates/assets/domain_gateway_list.html:72
#: assets/templates/assets/domain_list.html:28
#: assets/templates/assets/system_user_detail.html:104
#: assets/templates/assets/system_user_list.html:59
#: assets/templates/assets/user_asset_list.html:171 ops/models/adhoc.py:43
#: orgs/models.py:17 perms/models.py:59 perms/models.py:112
#: perms/templates/perms/asset_permission_detail.html:102 settings/models.py:34
#: terminal/models.py:32 terminal/templates/terminal/terminal_detail.html:63
#: users/models/group.py:15 users/models/user.py:94
#: users/templates/users/user_detail.html:127
#: users/templates/users/user_group_detail.html:67
#: users/templates/users/user_group_list.html:37
#: users/templates/users/user_profile.html:134
#: xpack/plugins/change_auth_plan/models.py:99
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:117
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:19
#: xpack/plugins/cloud/models.py:54 xpack/plugins/cloud/models.py:125
#: xpack/plugins/cloud/templates/cloud/account_detail.html:70
#: xpack/plugins/cloud/templates/cloud/account_list.html:15
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:69
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:16
#: xpack/plugins/orgs/templates/orgs/org_detail.html:64
#: xpack/plugins/orgs/templates/orgs/org_list.html:22
msgid "Comment"
msgstr "备注"
#: assets/models/asset.py:117 assets/models/base.py:38 #: assets/models/asset.py:117 assets/models/base.py:38
#: assets/templates/assets/admin_user_list.html:30 #: assets/serializers/admin_user.py:29 assets/serializers/system_user.py:22
#: assets/templates/assets/system_user_list.html:35 #: assets/templates/assets/admin_user_list.html:51
#: assets/templates/assets/system_user_list.html:57
msgid "Unreachable" msgid "Unreachable"
msgstr "不可达" msgstr "不可达"
#: assets/models/asset.py:118 assets/models/base.py:39 #: assets/models/asset.py:118 assets/models/base.py:39
#: assets/serializers/admin_user.py:32 assets/serializers/system_user.py:31
#: assets/templates/assets/admin_user_assets.html:51 #: assets/templates/assets/admin_user_assets.html:51
#: assets/templates/assets/admin_user_list.html:29 #: assets/templates/assets/admin_user_list.html:50
#: assets/templates/assets/asset_list.html:107
#: assets/templates/assets/asset_asset_user_list.html:46 #: assets/templates/assets/asset_asset_user_list.html:46
#: assets/templates/assets/asset_list.html:95
#: assets/templates/assets/system_user_asset.html:53 #: assets/templates/assets/system_user_asset.html:53
#: assets/templates/assets/system_user_list.html:34 #: assets/templates/assets/system_user_list.html:56
#: users/templates/users/user_group_granted_asset.html:47 #: users/templates/users/user_group_granted_asset.html:47
msgid "Reachable" msgid "Reachable"
msgstr "可连接" msgstr "可连接"
...@@ -1085,6 +1243,42 @@ msgstr "内容" ...@@ -1085,6 +1243,42 @@ msgstr "内容"
msgid "One line one command" msgid "One line one command"
msgstr "每行一个命令" msgstr "每行一个命令"
#: assets/models/cmd_filter.py:54
#: assets/templates/assets/admin_user_assets.html:52
#: assets/templates/assets/admin_user_list.html:54
#: assets/templates/assets/asset_list.html:108
#: assets/templates/assets/asset_asset_user_list.html:48
#: assets/templates/assets/cmd_filter_list.html:28
#: assets/templates/assets/cmd_filter_rule_list.html:63
#: assets/templates/assets/domain_gateway_list.html:73
#: assets/templates/assets/domain_list.html:29
#: assets/templates/assets/label_list.html:17
#: assets/templates/assets/system_user_asset.html:54
#: assets/templates/assets/system_user_list.html:60
#: assets/templates/assets/user_asset_list.html:48 audits/models.py:38
#: audits/templates/audits/operate_log_list.html:41
#: audits/templates/audits/operate_log_list.html:67
#: ops/templates/ops/adhoc_history.html:59 ops/templates/ops/task_adhoc.html:64
#: ops/templates/ops/task_history.html:65 ops/templates/ops/task_list.html:34
#: perms/forms.py:51 perms/models.py:21 perms/models.py:53
#: perms/templates/perms/asset_permission_create_update.html:50
#: perms/templates/perms/asset_permission_list.html:60
#: perms/templates/perms/asset_permission_list.html:134
#: settings/templates/settings/terminal_setting.html:82
#: settings/templates/settings/terminal_setting.html:104
#: terminal/templates/terminal/session_list.html:81
#: terminal/templates/terminal/terminal_list.html:36
#: users/templates/users/user_group_list.html:38
#: users/templates/users/user_list.html:41
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:60
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:18
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:20
#: xpack/plugins/cloud/templates/cloud/account_list.html:16
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:18
#: xpack/plugins/orgs/templates/orgs/org_list.html:23
msgid "Action"
msgstr "动作"
#: assets/models/cmd_filter.py:64 #: assets/models/cmd_filter.py:64
msgid "Command filter rule" msgid "Command filter rule"
msgstr "命令过滤规则" msgstr "命令过滤规则"
...@@ -1125,9 +1319,9 @@ msgstr "默认资产组" ...@@ -1125,9 +1319,9 @@ msgstr "默认资产组"
#: terminal/templates/terminal/command_list.html:72 #: terminal/templates/terminal/command_list.html:72
#: terminal/templates/terminal/session_list.html:33 #: terminal/templates/terminal/session_list.html:33
#: terminal/templates/terminal/session_list.html:71 users/forms.py:283 #: terminal/templates/terminal/session_list.html:71 users/forms.py:283
#: users/models/user.py:36 users/models/user.py:467 #: users/models/user.py:36 users/models/user.py:467 users/serializers/v1.py:61
#: users/templates/users/user_group_detail.html:78 #: users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_list.html:13 users/views/user.py:395 #: users/templates/users/user_group_list.html:36 users/views/user.py:395
#: xpack/plugins/orgs/forms.py:26 #: xpack/plugins/orgs/forms.py:26
#: xpack/plugins/orgs/templates/orgs/org_detail.html:113 #: xpack/plugins/orgs/templates/orgs/org_detail.html:113
#: xpack/plugins/orgs/templates/orgs/org_list.html:14 #: xpack/plugins/orgs/templates/orgs/org_list.html:14
...@@ -1174,7 +1368,7 @@ msgid "Shell" ...@@ -1174,7 +1368,7 @@ msgid "Shell"
msgstr "Shell" msgstr "Shell"
#: assets/models/user.py:140 assets/templates/assets/system_user_detail.html:66 #: assets/models/user.py:140 assets/templates/assets/system_user_detail.html:66
#: assets/templates/assets/system_user_list.html:32 #: assets/templates/assets/system_user_list.html:54
msgid "Login mode" msgid "Login mode"
msgstr "登录模式" msgstr "登录模式"
...@@ -1183,6 +1377,25 @@ msgstr "登录模式" ...@@ -1183,6 +1377,25 @@ msgstr "登录模式"
msgid "%(value)s is not an even number" msgid "%(value)s is not an even number"
msgstr "%(value)s is not an even number" msgstr "%(value)s is not an even number"
#: assets/serializers/admin_user.py:26
#: assets/templates/assets/asset_asset_user_list.html:51
#: assets/templates/assets/cmd_filter_detail.html:73
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:109
msgid "Date updated"
msgstr "更新日期"
#: assets/serializers/asset.py:20
msgid "Org name"
msgstr "组织名"
#: assets/serializers/asset.py:22
msgid "Hardware info"
msgstr "硬件信息"
#: assets/serializers/asset.py:25
msgid "Connectivity"
msgstr "连接"
#: assets/serializers/asset_user.py:23 users/forms.py:230 #: assets/serializers/asset_user.py:23 users/forms.py:230
#: users/models/user.py:91 users/templates/users/first_login.html:42 #: users/models/user.py:91 users/templates/users/first_login.html:42
#: users/templates/users/user_password_update.html:46 #: users/templates/users/user_password_update.html:46
...@@ -1192,6 +1405,18 @@ msgstr "%(value)s is not an even number" ...@@ -1192,6 +1405,18 @@ msgstr "%(value)s is not an even number"
msgid "Public key" msgid "Public key"
msgstr "ssh公钥" msgstr "ssh公钥"
#: assets/serializers/system_user.py:19
msgid "Login mode display"
msgstr "登录模式显示"
#: assets/serializers/system_user.py:25
msgid "Unreachable assets"
msgstr "不可达资产"
#: assets/serializers/system_user.py:29
msgid "Reachable assets"
msgstr "可连接资产"
#: assets/tasks.py:31 #: assets/tasks.py:31
msgid "Asset has been disabled, skipped: {}" msgid "Asset has been disabled, skipped: {}"
msgstr "资产或许不支持ansible, 跳过: {}" msgstr "资产或许不支持ansible, 跳过: {}"
...@@ -1261,6 +1486,15 @@ msgstr "推送系统用户到入资产: {} => {}" ...@@ -1261,6 +1486,15 @@ msgstr "推送系统用户到入资产: {} => {}"
msgid "Test asset user connectivity: {}" msgid "Test asset user connectivity: {}"
msgstr "测试资产用户可连接性: {}" msgstr "测试资产用户可连接性: {}"
#: assets/templates/assets/_admin_user_import_modal.html:4
msgid "Import admin user"
msgstr "导入管理用户"
#: assets/templates/assets/_admin_user_update_modal.html:4
#: assets/views/admin_user.py:64
msgid "Update admin user"
msgstr "更新管理用户"
#: assets/templates/assets/_asset_group_bulk_update_modal.html:5 #: assets/templates/assets/_asset_group_bulk_update_modal.html:5
msgid "Update asset group" msgid "Update asset group"
msgstr "更新用户组" msgstr "更新用户组"
...@@ -1290,32 +1524,18 @@ msgid "Enable-MFA" ...@@ -1290,32 +1524,18 @@ msgid "Enable-MFA"
msgstr "启用MFA" msgstr "启用MFA"
#: assets/templates/assets/_asset_import_modal.html:4 #: assets/templates/assets/_asset_import_modal.html:4
msgid "Import asset" msgid "Import assets"
msgstr "导入资产" msgstr "导入资产"
#: assets/templates/assets/_asset_import_modal.html:9
#: users/templates/users/_user_import_modal.html:10
msgid "Template"
msgstr "模板"
#: assets/templates/assets/_asset_import_modal.html:10
#: users/templates/users/_user_import_modal.html:11
msgid "Download"
msgstr "下载"
#: assets/templates/assets/_asset_import_modal.html:13
msgid "Asset csv file"
msgstr "资产csv文件"
#: assets/templates/assets/_asset_import_modal.html:16
msgid "If set id, will use this id update asset existed"
msgstr "如果设置了id,则会使用该行信息更新该id的资产"
#: assets/templates/assets/_asset_list_modal.html:7 assets/views/asset.py:52 #: assets/templates/assets/_asset_list_modal.html:7 assets/views/asset.py:52
#: templates/_nav.html:22 xpack/plugins/change_auth_plan/views.py:110 #: templates/_nav.html:22 xpack/plugins/change_auth_plan/views.py:110
msgid "Asset list" msgid "Asset list"
msgstr "资产列表" msgstr "资产列表"
#: assets/templates/assets/_asset_update_modal.html:4
msgid "Update assets"
msgstr "更新资产"
#: assets/templates/assets/_asset_user_auth_modal.html:4 #: assets/templates/assets/_asset_user_auth_modal.html:4
msgid "Update asset user auth" msgid "Update asset user auth"
msgstr "更新资产用户认证信息" msgstr "更新资产用户认证信息"
...@@ -1425,6 +1645,84 @@ msgstr "自动生成密钥" ...@@ -1425,6 +1645,84 @@ msgstr "自动生成密钥"
msgid "Other" msgid "Other"
msgstr "其它" msgstr "其它"
#: assets/templates/assets/_system_user.html:75
#: assets/templates/assets/admin_user_create_update.html:45
#: assets/templates/assets/asset_bulk_update.html:23
#: assets/templates/assets/asset_create.html:67
#: assets/templates/assets/asset_update.html:71
#: assets/templates/assets/cmd_filter_create_update.html:15
#: assets/templates/assets/cmd_filter_rule_create_update.html:40
#: assets/templates/assets/domain_create_update.html:16
#: assets/templates/assets/gateway_create_update.html:58
#: assets/templates/assets/label_create_update.html:18
#: perms/templates/perms/asset_permission_create_update.html:83
#: settings/templates/settings/basic_setting.html:61
#: settings/templates/settings/command_storage_create.html:79
#: settings/templates/settings/email_setting.html:62
#: settings/templates/settings/ldap_setting.html:61
#: settings/templates/settings/replay_storage_create.html:152
#: settings/templates/settings/security_setting.html:70
#: settings/templates/settings/terminal_setting.html:68
#: terminal/templates/terminal/terminal_update.html:45
#: users/templates/users/_user.html:50
#: users/templates/users/user_bulk_update.html:23
#: users/templates/users/user_detail.html:176
#: users/templates/users/user_password_update.html:71
#: users/templates/users/user_profile.html:204
#: users/templates/users/user_profile_update.html:63
#: users/templates/users/user_pubkey_update.html:70
#: users/templates/users/user_pubkey_update.html:76
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:71
#: xpack/plugins/cloud/templates/cloud/account_create_update.html:33
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create.html:35
#: xpack/plugins/interface/templates/interface/interface.html:72
msgid "Reset"
msgstr "重置"
#: assets/templates/assets/_system_user.html:76
#: assets/templates/assets/admin_user_create_update.html:46
#: assets/templates/assets/asset_bulk_update.html:24
#: assets/templates/assets/asset_create.html:68
#: assets/templates/assets/asset_list.html:125
#: assets/templates/assets/asset_update.html:72
#: assets/templates/assets/cmd_filter_create_update.html:16
#: assets/templates/assets/cmd_filter_rule_create_update.html:41
#: assets/templates/assets/domain_create_update.html:17
#: assets/templates/assets/gateway_create_update.html:59
#: assets/templates/assets/label_create_update.html:19
#: audits/templates/audits/login_log_list.html:89
#: perms/templates/perms/asset_permission_create_update.html:84
#: settings/templates/settings/basic_setting.html:62
#: settings/templates/settings/command_storage_create.html:80
#: settings/templates/settings/email_setting.html:63
#: settings/templates/settings/ldap_setting.html:64
#: settings/templates/settings/replay_storage_create.html:153
#: settings/templates/settings/security_setting.html:71
#: settings/templates/settings/terminal_setting.html:70
#: terminal/templates/terminal/command_list.html:103
#: terminal/templates/terminal/session_list.html:126
#: terminal/templates/terminal/terminal_update.html:46
#: users/templates/users/_user.html:51
#: users/templates/users/forgot_password.html:42
#: users/templates/users/user_bulk_update.html:24
#: users/templates/users/user_list.html:57
#: users/templates/users/user_password_update.html:72
#: users/templates/users/user_profile_update.html:64
#: users/templates/users/user_pubkey_update.html:77
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:72
#: xpack/plugins/interface/templates/interface/interface.html:74
msgid "Submit"
msgstr "提交"
#: assets/templates/assets/_system_user_import_modal.html:4
msgid "Import system user"
msgstr "导入系统用户"
#: assets/templates/assets/_system_user_update_modal.html:4
#: assets/views/system_user.py:61
msgid "Update system user"
msgstr "更新系统用户"
#: assets/templates/assets/_user_asset_detail_modal.html:11 #: assets/templates/assets/_user_asset_detail_modal.html:11
#: assets/templates/assets/asset_asset_user_list.html:13 #: assets/templates/assets/asset_asset_user_list.html:13
#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:187 #: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:187
...@@ -1495,6 +1793,82 @@ msgstr "更新成功" ...@@ -1495,6 +1793,82 @@ msgstr "更新成功"
msgid "Update failed!" msgid "Update failed!"
msgstr "更新失败" msgstr "更新失败"
#: assets/templates/assets/admin_user_detail.html:24
#: assets/templates/assets/admin_user_list.html:29
#: assets/templates/assets/admin_user_list.html:111
#: assets/templates/assets/asset_detail.html:27
#: assets/templates/assets/asset_list.html:86
#: assets/templates/assets/asset_list.html:190
#: assets/templates/assets/cmd_filter_detail.html:29
#: assets/templates/assets/cmd_filter_list.html:58
#: assets/templates/assets/cmd_filter_rule_list.html:86
#: assets/templates/assets/domain_detail.html:24
#: assets/templates/assets/domain_detail.html:103
#: assets/templates/assets/domain_gateway_list.html:97
#: assets/templates/assets/domain_list.html:54
#: assets/templates/assets/label_list.html:39
#: assets/templates/assets/system_user_detail.html:26
#: assets/templates/assets/system_user_list.html:33
#: assets/templates/assets/system_user_list.html:117 audits/models.py:33
#: perms/templates/perms/asset_permission_detail.html:30
#: perms/templates/perms/asset_permission_list.html:181
#: terminal/templates/terminal/terminal_detail.html:16
#: terminal/templates/terminal/terminal_list.html:72
#: users/templates/users/user_detail.html:25
#: users/templates/users/user_group_detail.html:28
#: users/templates/users/user_group_list.html:20
#: users/templates/users/user_group_list.html:69
#: users/templates/users/user_list.html:20
#: users/templates/users/user_list.html:96
#: users/templates/users/user_list.html:99
#: users/templates/users/user_profile.html:177
#: users/templates/users/user_profile.html:187
#: users/templates/users/user_profile.html:196
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:29
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:55
#: xpack/plugins/cloud/templates/cloud/account_detail.html:23
#: xpack/plugins/cloud/templates/cloud/account_list.html:39
#: xpack/plugins/orgs/templates/orgs/org_detail.html:25
#: xpack/plugins/orgs/templates/orgs/org_list.html:87
msgid "Update"
msgstr "更新"
#: assets/templates/assets/admin_user_detail.html:28
#: assets/templates/assets/admin_user_list.html:112
#: assets/templates/assets/asset_detail.html:31
#: assets/templates/assets/asset_list.html:191
#: assets/templates/assets/cmd_filter_detail.html:33
#: assets/templates/assets/cmd_filter_list.html:59
#: assets/templates/assets/cmd_filter_rule_list.html:87
#: assets/templates/assets/domain_detail.html:28
#: assets/templates/assets/domain_detail.html:104
#: assets/templates/assets/domain_gateway_list.html:98
#: assets/templates/assets/domain_list.html:55
#: assets/templates/assets/label_list.html:40
#: assets/templates/assets/system_user_detail.html:30
#: assets/templates/assets/system_user_list.html:118 audits/models.py:34
#: ops/templates/ops/task_list.html:64
#: perms/templates/perms/asset_permission_detail.html:34
#: perms/templates/perms/asset_permission_list.html:182
#: settings/templates/settings/terminal_setting.html:90
#: settings/templates/settings/terminal_setting.html:112
#: terminal/templates/terminal/terminal_list.html:74
#: users/templates/users/user_detail.html:30
#: users/templates/users/user_group_detail.html:32
#: users/templates/users/user_group_list.html:71
#: users/templates/users/user_list.html:104
#: users/templates/users/user_list.html:108
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:33
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:57
#: xpack/plugins/cloud/templates/cloud/account_detail.html:27
#: xpack/plugins/cloud/templates/cloud/account_list.html:41
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:30
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:55
#: xpack/plugins/orgs/templates/orgs/org_detail.html:29
#: xpack/plugins/orgs/templates/orgs/org_list.html:89
msgid "Delete"
msgstr "删除"
#: assets/templates/assets/admin_user_detail.html:83 #: assets/templates/assets/admin_user_detail.html:83
msgid "Replace node assets admin user with this" msgid "Replace node assets admin user with this"
msgstr "替换资产的管理员" msgstr "替换资产的管理员"
...@@ -1506,35 +1880,94 @@ msgstr "替换资产的管理员" ...@@ -1506,35 +1880,94 @@ msgstr "替换资产的管理员"
msgid "Select nodes" msgid "Select nodes"
msgstr "选择节点" msgstr "选择节点"
#: assets/templates/assets/admin_user_list.html:10 #: assets/templates/assets/admin_user_detail.html:100
#: assets/templates/assets/asset_detail.html:211
#: assets/templates/assets/asset_list.html:692
#: assets/templates/assets/cmd_filter_detail.html:106
#: assets/templates/assets/system_user_asset.html:112
#: assets/templates/assets/system_user_detail.html:182
#: assets/templates/assets/system_user_list.html:168
#: settings/templates/settings/terminal_setting.html:165
#: templates/_modal.html:23 terminal/templates/terminal/session_detail.html:108
#: users/templates/users/user_detail.html:388
#: users/templates/users/user_detail.html:414
#: users/templates/users/user_detail.html:437
#: users/templates/users/user_detail.html:482
#: users/templates/users/user_group_create_update.html:32
#: users/templates/users/user_group_list.html:114
#: users/templates/users/user_list.html:260
#: users/templates/users/user_profile.html:238
#: xpack/plugins/cloud/templates/cloud/account_create_update.html:34
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create.html:36
#: xpack/plugins/interface/templates/interface/interface.html:103
#: xpack/plugins/orgs/templates/orgs/org_create_update.html:33
msgid "Confirm"
msgstr "确认"
#: assets/templates/assets/admin_user_list.html:7
msgid "" msgid ""
"Admin users are asset (charged server) on the root, or have NOPASSWD: ALL " "Admin users are asset (charged server) on the root, or have NOPASSWD: ALL "
"sudo permissions users, " "sudo permissions users, "
msgstr "" msgstr ""
"管理用户是资产(被控服务器)上的root,或拥有 NOPASSWD: ALL sudo权限的用户," "管理用户是资产(被控服务器)上的root,或拥有 NOPASSWD: ALL sudo权限的用户,"
#: assets/templates/assets/admin_user_list.html:11 #: assets/templates/assets/admin_user_list.html:8
msgid "" msgid ""
"Jumpserver users of the system using the user to `push system user`, `get " "Jumpserver users of the system using the user to `push system user`, `get "
"assets hardware information`, etc. " "assets hardware information`, etc. "
msgstr "Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等。" msgstr "Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等。"
#: assets/templates/assets/admin_user_list.html:12 #: assets/templates/assets/admin_user_list.html:9
msgid "You can set any one for Windows or other hardware." msgid "You can set any one for Windows or other hardware."
msgstr "Windows或其它硬件可以随意设置一个" msgstr "Windows或其它硬件可以随意设置一个"
#: assets/templates/assets/admin_user_list.html:18 #: assets/templates/assets/admin_user_list.html:19
#: assets/templates/assets/asset_list.html:76
#: assets/templates/assets/system_user_list.html:23
#: audits/templates/audits/login_log_list.html:85
#: users/templates/users/user_group_list.html:10
#: users/templates/users/user_list.html:10
msgid "Export"
msgstr "导出"
#: assets/templates/assets/admin_user_list.html:24
#: assets/templates/assets/asset_list.html:81
#: assets/templates/assets/system_user_list.html:28
#: settings/templates/settings/_ldap_list_users_modal.html:97
#: users/templates/users/user_group_list.html:15
#: users/templates/users/user_list.html:15
#: xpack/plugins/license/templates/license/license_detail.html:110
msgid "Import"
msgstr "导入"
#: assets/templates/assets/admin_user_list.html:39
#: assets/views/admin_user.py:48 #: assets/views/admin_user.py:48
msgid "Create admin user" msgid "Create admin user"
msgstr "创建管理用户" msgstr "创建管理用户"
#: assets/templates/assets/admin_user_list.html:31 #: assets/templates/assets/admin_user_list.html:52
#: assets/templates/assets/system_user_list.html:36 #: assets/templates/assets/system_user_list.html:58
#: ops/templates/ops/adhoc_history.html:54 #: ops/templates/ops/adhoc_history.html:54
#: ops/templates/ops/task_history.html:60 #: ops/templates/ops/task_history.html:60
msgid "Ratio" msgid "Ratio"
msgstr "比例" msgstr "比例"
#: assets/templates/assets/admin_user_list.html:159
#: assets/templates/assets/admin_user_list.html:197
#: assets/templates/assets/asset_list.html:499
#: assets/templates/assets/asset_list.html:543
#: assets/templates/assets/system_user_list.html:226
#: assets/templates/assets/system_user_list.html:262
#: users/templates/users/user_group_list.html:163
#: users/templates/users/user_group_list.html:199
#: users/templates/users/user_list.html:162
#: users/templates/users/user_list.html:198
#, fuzzy
#| msgid "Please Select User"
msgid "Please select file"
msgstr "选择用户"
#: assets/templates/assets/asset_asset_user_list.html:16 #: assets/templates/assets/asset_asset_user_list.html:16
#: assets/templates/assets/asset_detail.html:23 assets/views/asset.py:68 #: assets/templates/assets/asset_detail.html:23 assets/views/asset.py:68
msgid "Asset user list" msgid "Asset user list"
...@@ -1548,6 +1981,7 @@ msgstr "资产用户" ...@@ -1548,6 +1981,7 @@ msgstr "资产用户"
msgid "Password version" msgid "Password version"
msgstr "密码版本" msgstr "密码版本"
#: assets/templates/assets/asset_asset_user_list.html:47 #: assets/templates/assets/asset_asset_user_list.html:47
#: assets/templates/assets/cmd_filter_detail.html:73 #: assets/templates/assets/cmd_filter_detail.html:73
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:109 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:109
...@@ -1626,146 +2060,133 @@ msgstr "" ...@@ -1626,146 +2060,133 @@ msgstr ""
msgid "Create asset" msgid "Create asset"
msgstr "创建资产" msgstr "创建资产"
#: assets/templates/assets/asset_list.html:73 #: assets/templates/assets/asset_list.html:106
#: settings/templates/settings/_ldap_list_users_modal.html:97
#: users/templates/users/user_list.html:7
#: xpack/plugins/license/templates/license/license_detail.html:110
msgid "Import"
msgstr "导入"
#: assets/templates/assets/asset_list.html:76
#: audits/templates/audits/login_log_list.html:85
#: users/templates/users/user_list.html:10
msgid "Export"
msgstr "导出"
#: assets/templates/assets/asset_list.html:94
msgid "Hardware" msgid "Hardware"
msgstr "硬件" msgstr "硬件"
#: assets/templates/assets/asset_list.html:105 #: assets/templates/assets/asset_list.html:117
#: users/templates/users/user_list.html:38 #: users/templates/users/user_list.html:50
msgid "Delete selected" msgid "Delete selected"
msgstr "批量删除" msgstr "批量删除"
#: assets/templates/assets/asset_list.html:106 #: assets/templates/assets/asset_list.html:118
#: users/templates/users/user_list.html:39 #: users/templates/users/user_list.html:51
msgid "Update selected" msgid "Update selected"
msgstr "批量更新" msgstr "批量更新"
#: assets/templates/assets/asset_list.html:107 #: assets/templates/assets/asset_list.html:119
msgid "Remove from this node" msgid "Remove from this node"
msgstr "从节点移除" msgstr "从节点移除"
#: assets/templates/assets/asset_list.html:108 #: assets/templates/assets/asset_list.html:120
#: users/templates/users/user_list.html:40 #: users/templates/users/user_list.html:52
msgid "Deactive selected" msgid "Deactive selected"
msgstr "禁用所选" msgstr "禁用所选"
#: assets/templates/assets/asset_list.html:109 #: assets/templates/assets/asset_list.html:121
#: users/templates/users/user_list.html:41 #: users/templates/users/user_list.html:53
msgid "Active selected" msgid "Active selected"
msgstr "激活所选" msgstr "激活所选"
#: assets/templates/assets/asset_list.html:126 #: assets/templates/assets/asset_list.html:138
msgid "Add node" msgid "Add node"
msgstr "新建节点" msgstr "新建节点"
#: assets/templates/assets/asset_list.html:127 #: assets/templates/assets/asset_list.html:139
msgid "Rename node" msgid "Rename node"
msgstr "重命名节点" msgstr "重命名节点"
#: assets/templates/assets/asset_list.html:128 #: assets/templates/assets/asset_list.html:140
msgid "Delete node" msgid "Delete node"
msgstr "删除节点" msgstr "删除节点"
#: assets/templates/assets/asset_list.html:130 #: assets/templates/assets/asset_list.html:142
msgid "Add assets to node" msgid "Add assets to node"
msgstr "添加资产到节点" msgstr "添加资产到节点"
#: assets/templates/assets/asset_list.html:131 #: assets/templates/assets/asset_list.html:143
msgid "Move assets to node" msgid "Move assets to node"
msgstr "移动资产到节点" msgstr "移动资产到节点"
#: assets/templates/assets/asset_list.html:133 #: assets/templates/assets/asset_list.html:145
msgid "Refresh node hardware info" msgid "Refresh node hardware info"
msgstr "更新节点资产硬件信息" msgstr "更新节点资产硬件信息"
#: assets/templates/assets/asset_list.html:134 #: assets/templates/assets/asset_list.html:146
msgid "Test node connective" msgid "Test node connective"
msgstr "测试节点资产可连接性" msgstr "测试节点资产可连接性"
#: assets/templates/assets/asset_list.html:136 #: assets/templates/assets/asset_list.html:148
msgid "Refresh all node assets amount" msgid "Refresh all node assets amount"
msgstr "刷新所有节点资产数量" msgstr "刷新所有节点资产数量"
#: assets/templates/assets/asset_list.html:138 #: assets/templates/assets/asset_list.html:150
msgid "Display only current node assets" msgid "Display only current node assets"
msgstr "仅显示当前节点资产" msgstr "仅显示当前节点资产"
#: assets/templates/assets/asset_list.html:139 #: assets/templates/assets/asset_list.html:151
msgid "Displays all child node assets" msgid "Displays all child node assets"
msgstr "显示所有子节点资产" msgstr "显示所有子节点资产"
#: assets/templates/assets/asset_list.html:217 #: assets/templates/assets/asset_list.html:229
msgid "Create node failed" msgid "Create node failed"
msgstr "创建节点失败" msgstr "创建节点失败"
#: assets/templates/assets/asset_list.html:229 #: assets/templates/assets/asset_list.html:241
msgid "Have child node, cancel" msgid "Have child node, cancel"
msgstr "存在子节点,不能删除" msgstr "存在子节点,不能删除"
#: assets/templates/assets/asset_list.html:231 #: assets/templates/assets/asset_list.html:243
msgid "Have assets, cancel" msgid "Have assets, cancel"
msgstr "存在资产,不能删除" msgstr "存在资产,不能删除"
#: assets/templates/assets/asset_list.html:302 #: assets/templates/assets/asset_list.html:314
msgid "Rename success" msgid "Rename success"
msgstr "重命名成功" msgstr "重命名成功"
#: assets/templates/assets/asset_list.html:303 #: assets/templates/assets/asset_list.html:315
msgid "Rename failed, do not change the root node name" msgid "Rename failed, do not change the root node name"
msgstr "重命名失败,不能更改root节点的名称" msgstr "重命名失败,不能更改root节点的名称"
#: assets/templates/assets/asset_list.html:631 #: assets/templates/assets/asset_list.html:686
#: assets/templates/assets/system_user_list.html:138 #: assets/templates/assets/system_user_list.html:162
#: users/templates/users/user_detail.html:382 #: users/templates/users/user_detail.html:382
#: users/templates/users/user_detail.html:408 #: users/templates/users/user_detail.html:408
#: users/templates/users/user_detail.html:476 #: users/templates/users/user_detail.html:476
#: users/templates/users/user_group_list.html:84 #: users/templates/users/user_group_list.html:108
#: users/templates/users/user_list.html:209 #: users/templates/users/user_list.html:254
#: xpack/plugins/interface/templates/interface/interface.html:97 #: xpack/plugins/interface/templates/interface/interface.html:97
msgid "Are you sure?" msgid "Are you sure?"
msgstr "你确认吗?" msgstr "你确认吗?"
#: assets/templates/assets/asset_list.html:632 #: assets/templates/assets/asset_list.html:687
msgid "This will delete the selected assets !!!" msgid "This will delete the selected assets !!!"
msgstr "删除选择资产" msgstr "删除选择资产"
#: assets/templates/assets/asset_list.html:635 #: assets/templates/assets/asset_list.html:690
#: assets/templates/assets/system_user_list.html:142 #: assets/templates/assets/system_user_list.html:166
#: settings/templates/settings/terminal_setting.html:163 #: settings/templates/settings/terminal_setting.html:163
#: users/templates/users/user_detail.html:386 #: users/templates/users/user_detail.html:386
#: users/templates/users/user_detail.html:412 #: users/templates/users/user_detail.html:412
#: users/templates/users/user_detail.html:480 #: users/templates/users/user_detail.html:480
#: users/templates/users/user_group_create_update.html:31 #: users/templates/users/user_group_create_update.html:31
#: users/templates/users/user_group_list.html:88 #: users/templates/users/user_group_list.html:112
#: users/templates/users/user_list.html:213 #: users/templates/users/user_list.html:258
#: xpack/plugins/interface/templates/interface/interface.html:101 #: xpack/plugins/interface/templates/interface/interface.html:101
#: xpack/plugins/orgs/templates/orgs/org_create_update.html:32 #: xpack/plugins/orgs/templates/orgs/org_create_update.html:32
msgid "Cancel" msgid "Cancel"
msgstr "取消" msgstr "取消"
#: assets/templates/assets/asset_list.html:641 #: assets/templates/assets/asset_list.html:696
msgid "Asset Deleted." msgid "Asset Deleted."
msgstr "已被删除" msgstr "已被删除"
#: assets/templates/assets/asset_list.html:642 #: assets/templates/assets/asset_list.html:697
#: assets/templates/assets/asset_list.html:647 #: assets/templates/assets/asset_list.html:702
msgid "Asset Delete" msgid "Asset Delete"
msgstr "删除" msgstr "删除"
#: assets/templates/assets/asset_list.html:646 #: assets/templates/assets/asset_list.html:701
msgid "Asset Deleting failed." msgid "Asset Deleting failed."
msgstr "删除失败" msgstr "删除失败"
...@@ -1948,25 +2369,25 @@ msgstr "" ...@@ -1948,25 +2369,25 @@ msgstr ""
"资产中,如果资产(交换机、windows)不支持ansible, 请手动填写账号密码。目前还不" "资产中,如果资产(交换机、windows)不支持ansible, 请手动填写账号密码。目前还不"
"支持Windows的自动推送" "支持Windows的自动推送"
#: assets/templates/assets/system_user_list.html:21 #: assets/templates/assets/system_user_list.html:43
#: assets/views/system_user.py:45 #: assets/views/system_user.py:45
msgid "Create system user" msgid "Create system user"
msgstr "创建系统用户" msgstr "创建系统用户"
#: assets/templates/assets/system_user_list.html:139 #: assets/templates/assets/system_user_list.html:163
msgid "This will delete the selected System Users !!!" msgid "This will delete the selected System Users !!!"
msgstr "删除选择系统用户" msgstr "删除选择系统用户"
#: assets/templates/assets/system_user_list.html:148 #: assets/templates/assets/system_user_list.html:172
msgid "System Users Deleted." msgid "System Users Deleted."
msgstr "已被删除" msgstr "已被删除"
#: assets/templates/assets/system_user_list.html:149 #: assets/templates/assets/system_user_list.html:173
#: assets/templates/assets/system_user_list.html:154 #: assets/templates/assets/system_user_list.html:178
msgid "System Users Delete" msgid "System Users Delete"
msgstr "删除系统用户" msgstr "删除系统用户"
#: assets/templates/assets/system_user_list.html:153 #: assets/templates/assets/system_user_list.html:177
msgid "System Users Deleting failed." msgid "System Users Deleting failed."
msgstr "系统用户删除失败" msgstr "系统用户删除失败"
...@@ -1974,10 +2395,6 @@ msgstr "系统用户删除失败" ...@@ -1974,10 +2395,6 @@ msgstr "系统用户删除失败"
msgid "Admin user list" msgid "Admin user list"
msgstr "管理用户列表" msgstr "管理用户列表"
#: assets/views/admin_user.py:64
msgid "Update admin user"
msgstr "更新管理用户"
#: assets/views/admin_user.py:79 assets/views/admin_user.py:103 #: assets/views/admin_user.py:79 assets/views/admin_user.py:103
msgid "Admin user detail" msgid "Admin user detail"
msgstr "管理用户详情" msgstr "管理用户详情"
...@@ -2058,10 +2475,6 @@ msgstr "更新标签" ...@@ -2058,10 +2475,6 @@ msgstr "更新标签"
msgid "System user list" msgid "System user list"
msgstr "系统用户列表" msgstr "系统用户列表"
#: assets/views/system_user.py:61
msgid "Update system user"
msgstr "更新系统用户"
#: assets/views/system_user.py:75 #: assets/views/system_user.py:75
msgid "System user detail" msgid "System user detail"
msgstr "系统用户详情" msgstr "系统用户详情"
...@@ -2551,11 +2964,11 @@ msgstr "" ...@@ -2551,11 +2964,11 @@ msgstr ""
msgid "Encrypt field using Secret Key" msgid "Encrypt field using Secret Key"
msgstr "" msgstr ""
#: common/mixins.py:32 #: common/mixins.py:35
msgid "is discard" msgid "is discard"
msgstr "" msgstr ""
#: common/mixins.py:33 #: common/mixins.py:36
msgid "discard time" msgid "discard time"
msgstr "" msgstr ""
...@@ -2914,7 +3327,7 @@ msgstr "命令执行列表" ...@@ -2914,7 +3327,7 @@ msgstr "命令执行列表"
msgid "Command execution" msgid "Command execution"
msgstr "命令执行" msgstr "命令执行"
#: orgs/mixins.py:77 orgs/models.py:24 #: orgs/mixins.py:81 orgs/models.py:24
msgid "Organization" msgid "Organization"
msgstr "组织管理" msgstr "组织管理"
...@@ -2940,7 +3353,7 @@ msgstr "下载文件" ...@@ -2940,7 +3353,7 @@ msgstr "下载文件"
#: templates/_nav.html:14 users/forms.py:253 users/models/group.py:26 #: templates/_nav.html:14 users/forms.py:253 users/models/group.py:26
#: users/models/user.py:67 users/templates/users/_select_user_modal.html:16 #: users/models/user.py:67 users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_detail.html:213 #: users/templates/users/user_detail.html:213
#: users/templates/users/user_list.html:26 #: users/templates/users/user_list.html:38
#: xpack/plugins/orgs/templates/orgs/org_list.html:15 #: xpack/plugins/orgs/templates/orgs/org_list.html:15
msgid "User group" msgid "User group"
msgstr "用户组" msgstr "用户组"
...@@ -3065,9 +3478,9 @@ msgstr "创建授权规则" ...@@ -3065,9 +3478,9 @@ msgstr "创建授权规则"
#: perms/templates/perms/asset_permission_list.html:59 #: perms/templates/perms/asset_permission_list.html:59
#: perms/templates/perms/asset_permission_list.html:73 #: perms/templates/perms/asset_permission_list.html:73
#: users/templates/users/user_list.html:40 xpack/plugins/cloud/models.py:53
#: xpack/plugins/cloud/templates/cloud/account_detail.html:60
#: perms/templates/perms/remote_app_permission_list.html:18 #: perms/templates/perms/remote_app_permission_list.html:18
#: users/templates/users/user_list.html:28 xpack/plugins/cloud/models.py:53
#: xpack/plugins/cloud/templates/cloud/account_detail.html:58
#: xpack/plugins/cloud/templates/cloud/account_list.html:14 #: xpack/plugins/cloud/templates/cloud/account_list.html:14
msgid "Validity" msgid "Validity"
msgstr "有效" msgstr "有效"
...@@ -3700,6 +4113,18 @@ msgstr "注销登录" ...@@ -3700,6 +4113,18 @@ msgstr "注销登录"
msgid "Dashboard" msgid "Dashboard"
msgstr "仪表盘" msgstr "仪表盘"
#: templates/_import_modal.html:12
msgid "Download the imported template or use the exported CSV file format"
msgstr "下载导入的模板或使用导出的csv格式"
#: templates/_import_modal.html:13
msgid "Download the import template"
msgstr "下载导入模版"
#: templates/_import_modal.html:17 templates/_update_modal.html:17
msgid "Select the CSV file to import"
msgstr "请选择csv文件导入"
#: templates/_message.html:7 #: templates/_message.html:7
#, python-format #, python-format
msgid "" msgid ""
...@@ -3840,6 +4265,14 @@ msgid "" ...@@ -3840,6 +4265,14 @@ msgid ""
"Displays the results of items _START_ to _END_; A total of _TOTAL_ entries" "Displays the results of items _START_ to _END_; A total of _TOTAL_ entries"
msgstr "显示第 _START_ 至 _END_ 项结果; 总共 _TOTAL_ 项" msgstr "显示第 _START_ 至 _END_ 项结果; 总共 _TOTAL_ 项"
#: templates/_update_modal.html:12
msgid "Download the update template or use the exported CSV file format"
msgstr "下载更新的模板或使用导出的csv格式"
#: templates/_update_modal.html:13
msgid "Download the update template"
msgstr "下载更新模版"
#: templates/captcha/image.html:3 #: templates/captcha/image.html:3
msgid "Play CAPTCHA as audio file" msgid "Play CAPTCHA as audio file"
msgstr "语言播放验证码" msgstr "语言播放验证码"
...@@ -4206,18 +4639,18 @@ msgid "" ...@@ -4206,18 +4639,18 @@ msgid ""
"You should use your ssh client tools connect terminal: {} <br /> <br />{}" "You should use your ssh client tools connect terminal: {} <br /> <br />{}"
msgstr "你可以使用ssh客户端工具连接终端" msgstr "你可以使用ssh客户端工具连接终端"
#: users/api/user.py:69 users/api/user.py:80 users/api/user.py:106 #: users/api/user.py:77 users/api/user.py:88 users/api/user.py:114
msgid "You do not have permission." msgid "You do not have permission."
msgstr "你没有权限" msgstr "你没有权限"
#: users/api/user.py:210 #: users/api/user.py:218
msgid "Could not reset self otp, use profile reset instead" msgid "Could not reset self otp, use profile reset instead"
msgstr "不能再该页面重置MFA, 请去个人信息页面重置" msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
#: users/forms.py:32 users/models/user.py:71 #: users/forms.py:32 users/models/user.py:71
#: users/templates/users/_select_user_modal.html:15 #: users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:87 #: users/templates/users/user_detail.html:87
#: users/templates/users/user_list.html:25 #: users/templates/users/user_list.html:37
#: users/templates/users/user_profile.html:55 #: users/templates/users/user_profile.html:55
msgid "Role" msgid "Role"
msgstr "角色" msgstr "角色"
...@@ -4242,7 +4675,7 @@ msgstr "添加到用户组" ...@@ -4242,7 +4675,7 @@ msgstr "添加到用户组"
msgid "Public key should not be the same as your old one." msgid "Public key should not be the same as your old one."
msgstr "不能和原来的密钥相同" msgstr "不能和原来的密钥相同"
#: users/forms.py:89 users/forms.py:219 users/serializers/v1.py:38 #: users/forms.py:89 users/forms.py:219 users/serializers/v1.py:53
msgid "Not a valid ssh public key" msgid "Not a valid ssh public key"
msgstr "ssh密钥不合法" msgstr "ssh密钥不合法"
...@@ -4339,7 +4772,7 @@ msgid "Wechat" ...@@ -4339,7 +4772,7 @@ msgid "Wechat"
msgstr "微信" msgstr "微信"
#: users/models/user.py:106 users/templates/users/user_detail.html:103 #: users/models/user.py:106 users/templates/users/user_detail.html:103
#: users/templates/users/user_list.html:27 #: users/templates/users/user_list.html:39
#: users/templates/users/user_profile.html:100 #: users/templates/users/user_profile.html:100
msgid "Source" msgid "Source"
msgstr "用户来源" msgstr "用户来源"
...@@ -4357,6 +4790,34 @@ msgstr "用户认证源来自 {}, 请去相应系统修改密码" ...@@ -4357,6 +4790,34 @@ msgstr "用户认证源来自 {}, 请去相应系统修改密码"
msgid "Administrator is the super user of system" msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员" msgstr "Administrator是初始的超级管理员"
#: users/serializers/v1.py:17
msgid "Groups name"
msgstr "用户组名"
#: users/serializers/v1.py:20
msgid "Source name"
msgstr "用户来源名"
#: users/serializers/v1.py:23
msgid "Is first login"
msgstr "首次登录"
#: users/serializers/v1.py:25
msgid "Role name"
msgstr "角色名"
#: users/serializers/v1.py:26
msgid "Is valid"
msgstr "账户是否有效"
#: users/serializers/v1.py:27
msgid "Is expired"
msgstr " 是否过期"
#: users/serializers/v1.py:28
msgid "Avatar url"
msgstr "头像路径"
#: users/serializers_v2/user.py:36 #: users/serializers_v2/user.py:36
msgid "name not unique" msgid "name not unique"
msgstr "名称重复" msgstr "名称重复"
...@@ -4393,21 +4854,22 @@ msgstr "资产数量" ...@@ -4393,21 +4854,22 @@ msgstr "资产数量"
msgid "Security and Role" msgid "Security and Role"
msgstr "角色安全" msgstr "角色安全"
#: users/templates/users/_user_import_modal.html:4 #: users/templates/users/_user_groups_import_modal.html:4
msgid "Import user" msgid "Import user groups"
msgstr "导入" msgstr "导入用户组"
#: users/templates/users/_user_import_modal.html:6 #: users/templates/users/_user_groups_update_modal.html:4
msgid "Download template or use export csv format" msgid "Update user groups"
msgstr "下载模板或使用导出的csv格式" msgstr "更新用户组"
#: users/templates/users/_user_import_modal.html:14 #: users/templates/users/_user_import_modal.html:4
msgid "Users csv file" msgid "Import users"
msgstr "用户csv文件" msgstr "导入用户"
#: users/templates/users/_user_import_modal.html:16 #: users/templates/users/_user_update_modal.html:4
msgid "If set id, will use this id update user existed" #: users/templates/users/user_update.html:4 users/views/user.py:123
msgstr "如果设置了id,则会使用该行信息更新该id的用户" msgid "Update user"
msgstr "更新用户"
#: users/templates/users/_user_update_pk_modal.html:4 #: users/templates/users/_user_update_pk_modal.html:4
msgid "Update User SSH Public Key" msgid "Update User SSH Public Key"
...@@ -4535,7 +4997,7 @@ msgid "Very strong" ...@@ -4535,7 +4997,7 @@ msgid "Very strong"
msgstr "很强" msgstr "很强"
#: users/templates/users/user_create.html:4 #: users/templates/users/user_create.html:4
#: users/templates/users/user_list.html:16 users/views/user.py:83 #: users/templates/users/user_list.html:28 users/views/user.py:83
msgid "Create user" msgid "Create user"
msgstr "创建用户" msgstr "创建用户"
...@@ -4656,49 +5118,49 @@ msgstr "用户组详情" ...@@ -4656,49 +5118,49 @@ msgstr "用户组详情"
msgid "Add user" msgid "Add user"
msgstr "添加用户" msgstr "添加用户"
#: users/templates/users/user_group_list.html:5 users/views/group.py:44 #: users/templates/users/user_group_list.html:28 users/views/group.py:44
msgid "Create user group" msgid "Create user group"
msgstr "创建用户组" msgstr "创建用户组"
#: users/templates/users/user_group_list.html:85 #: users/templates/users/user_group_list.html:109
msgid "This will delete the selected groups !!!" msgid "This will delete the selected groups !!!"
msgstr "删除选择组" msgstr "删除选择组"
#: users/templates/users/user_group_list.html:94 #: users/templates/users/user_group_list.html:118
msgid "UserGroups Deleted." msgid "UserGroups Deleted."
msgstr "用户组删除" msgstr "用户组删除"
#: users/templates/users/user_group_list.html:95 #: users/templates/users/user_group_list.html:119
#: users/templates/users/user_group_list.html:100 #: users/templates/users/user_group_list.html:124
msgid "UserGroups Delete" msgid "UserGroups Delete"
msgstr "用户组删除" msgstr "用户组删除"
#: users/templates/users/user_group_list.html:99 #: users/templates/users/user_group_list.html:123
msgid "UserGroup Deleting failed." msgid "UserGroup Deleting failed."
msgstr "用户组删除失败" msgstr "用户组删除失败"
#: users/templates/users/user_list.html:210 #: users/templates/users/user_list.html:255
msgid "This will delete the selected users !!!" msgid "This will delete the selected users !!!"
msgstr "删除选中用户 !!!" msgstr "删除选中用户 !!!"
#: users/templates/users/user_list.html:219 #: users/templates/users/user_list.html:264
msgid "User Deleted." msgid "User Deleted."
msgstr "已被删除" msgstr "已被删除"
#: users/templates/users/user_list.html:220 #: users/templates/users/user_list.html:265
#: users/templates/users/user_list.html:225 #: users/templates/users/user_list.html:270
msgid "User Delete" msgid "User Delete"
msgstr "删除" msgstr "删除"
#: users/templates/users/user_list.html:224 #: users/templates/users/user_list.html:269
msgid "User Deleting failed." msgid "User Deleting failed."
msgstr "用户删除失败" msgstr "用户删除失败"
#: users/templates/users/user_list.html:260 #: users/templates/users/user_list.html:305
msgid "User is expired" msgid "User is expired"
msgstr "用户已失效" msgstr "用户已失效"
#: users/templates/users/user_list.html:263 #: users/templates/users/user_list.html:308
msgid "User is inactive" msgid "User is inactive"
msgstr "用户已禁用" msgstr "用户已禁用"
...@@ -4798,10 +5260,6 @@ msgid "" ...@@ -4798,10 +5260,6 @@ msgid ""
"corresponding private key." "corresponding private key."
msgstr "新的公钥已设置成功,请下载对应的私钥" msgstr "新的公钥已设置成功,请下载对应的私钥"
#: users/templates/users/user_update.html:4 users/views/user.py:123
msgid "Update user"
msgstr "更新用户"
#: users/utils.py:38 #: users/utils.py:38
msgid "Create account successfully" msgid "Create account successfully"
msgstr "创建账户成功" msgstr "创建账户成功"
...@@ -5728,6 +6186,25 @@ msgstr "创建组织" ...@@ -5728,6 +6186,25 @@ msgstr "创建组织"
msgid "Update org" msgid "Update org"
msgstr "更新组织" msgstr "更新组织"
#~ msgid "Template"
#~ msgstr "模板"
#~ msgid "Download"
#~ msgstr "下载"
#~ msgid "Asset csv file"
#~ msgstr "资产csv文件"
#~ msgid "If set id, will use this id update asset existed"
#~ msgstr "如果设置了id,则会使用该行信息更新该id的资产"
#~ msgid "Users csv file"
#~ msgstr "用户csv文件"
#~ msgid "If set id, will use this id update user existed"
#~ msgstr "如果设置了id,则会使用该行信息更新该id的用户"
#~ msgid "MFA Confirm" #~ msgid "MFA Confirm"
#~ msgstr "确认" #~ msgstr "确认"
...@@ -5747,6 +6224,7 @@ msgstr "更新组织" ...@@ -5747,6 +6224,7 @@ msgstr "更新组织"
#~ msgid "Restore default successfully!" #~ msgid "Restore default successfully!"
#~ msgstr "恢复默认成功!" #~ msgstr "恢复默认成功!"
#~ msgid "Beijing Duizhan Tech, Inc." #~ msgid "Beijing Duizhan Tech, Inc."
#~ msgstr "北京堆栈科技有限公司" #~ msgstr "北京堆栈科技有限公司"
...@@ -5780,6 +6258,24 @@ msgstr "更新组织" ...@@ -5780,6 +6258,24 @@ msgstr "更新组织"
#~ msgid "Invalid private key" #~ msgid "Invalid private key"
#~ msgstr "ssh密钥不合法" #~ msgstr "ssh密钥不合法"
#, fuzzy
#~| msgid "CPU count"
#~ msgid "Cpu count"
#~ msgstr "CPU数量"
#~ msgid "Login Jumpserver"
#~ msgstr "登录 Jumpserver"
#, fuzzy
#~| msgid "Delete succeed"
#~ msgid "Delete success!"
#~ msgstr "删除成功"
#, fuzzy
#~| msgid "Username does not exist"
#~ msgid "This license does not exist!"
#~ msgstr "用户名不存在"
#~ msgid "Valid" #~ msgid "Valid"
#~ msgstr "账户状态" #~ msgstr "账户状态"
......
...@@ -8,9 +8,12 @@ from django.shortcuts import redirect, get_object_or_404 ...@@ -8,9 +8,12 @@ from django.shortcuts import redirect, get_object_or_404
from django.forms import ModelForm from django.forms import ModelForm
from django.http.response import HttpResponseForbidden from django.http.response import HttpResponseForbidden
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from rest_framework import serializers
from common.utils import get_logger from common.utils import get_logger
from .utils import current_org, set_current_org, set_to_root_org from .utils import (
current_org, set_current_org, set_to_root_org, get_current_org_id
)
from .models import Organization from .models import Organization
logger = get_logger(__file__) logger = get_logger(__file__)
...@@ -18,7 +21,8 @@ tl = Local() ...@@ -18,7 +21,8 @@ tl = Local()
__all__ = [ __all__ = [
'OrgManager', 'OrgViewGenericMixin', 'OrgModelMixin', 'OrgModelForm', 'OrgManager', 'OrgViewGenericMixin', 'OrgModelMixin', 'OrgModelForm',
'RootOrgViewMixin', 'OrgMembershipSerializerMixin', 'OrgMembershipModelViewSetMixin' 'RootOrgViewMixin', 'OrgMembershipSerializerMixin',
'OrgMembershipModelViewSetMixin', 'OrgResourceSerializerMixin',
] ]
...@@ -202,3 +206,11 @@ class OrgMembershipModelViewSetMixin: ...@@ -202,3 +206,11 @@ class OrgMembershipModelViewSetMixin:
def get_queryset(self): def get_queryset(self):
queryset = self.membership_class.objects.filter(organization=self.org) queryset = self.membership_class.objects.filter(organization=self.org)
return queryset return queryset
class OrgResourceSerializerMixin(serializers.Serializer):
"""
通过API批量操作资源时, 自动给每个资源添加所需属性org_id的值为current_org_id
(同时为serializer.is_valid()对Model的unique_together校验做准备)
"""
org_id = serializers.HiddenField(default=get_current_org_id)
...@@ -38,4 +38,10 @@ def get_current_org(): ...@@ -38,4 +38,10 @@ def get_current_org():
return _find('current_org') return _find('current_org')
def get_current_org_id():
org = get_current_org()
org_id = str(org.id) if org.is_real() else ''
return org_id
current_org = LocalProxy(partial(_find, 'current_org')) current_org = LocalProxy(partial(_find, 'current_org'))
...@@ -954,9 +954,92 @@ function rootNodeAddDom(ztree, callback) { ...@@ -954,9 +954,92 @@ function rootNodeAddDom(ztree, callback) {
}) })
} }
function APIExportData(props) {
props = props || {};
$.ajax({
url: '/api/common/v1/resources/cache/',
type: props.method || "POST",
data: props.body,
contentType: props.content_type || "application/json; charset=utf-8",
dataType: props.data_type || "json",
success: function (data) {
var export_url = props.success_url;
var params = props.params || {};
params['format'] = props.format;
params['spm'] = data.spm;
for (var k in params){
export_url = setUrlParam(export_url, k, params[k])
}
window.open(export_url);
},
error: function () {
toastr.error('Export failed');
}
})
}
function APIImportData(props){
props = props || {};
$.ajax({
url: props.url,
type: props.method || "POST",
processData: false,
data: props.body,
contentType: props.content_type || 'text/csv',
success: function (data) {
if(props.method === 'POST'){
$('#created_failed').html('');
$('#created_failed_detail').html('');
$('#success_created').html("Import Success");
$('#success_created_detail').html("Count" + ": " + data.length);
}else{
$('#updated_failed').html('');
$('#updated_failed_detail').html('');
$('#success_updated').html("Update Success");
$('#success_updated_detail').html("Count" + ": " + data.length);
}
props.data_table.ajax.reload()
},
error: function (error) {
var data = error.responseJSON;
if (data instanceof Array){
var html = '';
var li = '';
var err = '';
$.each(data, function (index, item){
err = '';
for (var prop in item) {
err += prop + ": " + item[prop][0] + " "
}
if (err) {
li = "<li>" + "Line " + (++index) + ". " + err + "</li>";
html += li
}
});
html = "<ul>" + html + "</ul>"
}
else {
html = error.responseText
}
if(props.method === 'POST'){
$('#success_created').html('');
$('#success_created_detail').html('');
$('#created_failed').html("Import failed");
$('#created_failed_detail').html(html);
}else{
$('#success_updated').html('');
$('#success_updated_detail').html('');
$('#updated_failed').html("Update failed");
$('#updated_failed_detail').html(html);
}
}
})
}
function htmlEscape ( d ) { function htmlEscape ( d ) {
return typeof d === 'string' ? return typeof d === 'string' ?
d.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;') : d.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;') :
d; d;
} }
\ No newline at end of file
{% extends '_modal.html' %}
{% load i18n %}
{% block modal_id %}import_modal{% endblock %}
{% block modal_confirm_id %}btn_import_confirm{% endblock %}
{% block modal_body %}
<form method="post" id="fm_import">
{% csrf_token %}
<div class="form-group">
<label class="control-label">{% trans "Download the imported template or use the exported CSV file format" %}</label>
<a href="{% block import_modal_download_template_url %}{% endblock %}?format=csv&template=import" style="display: block">{% trans 'Download the import template' %}</a>
</div>
<div class="form-group">
<label class="control-label" for="id_file">{% trans "Select the CSV file to import" %}</label>
<input id="id_file" type="file" name="file" />
</div>
</form>
<div>
<p class="text-success" id="success_created"></p>
<p id="success_created_detail"></p>
<p class="text-danger" id="created_failed"></p>
<p id="created_failed_detail"></p>
</div>
{% endblock %}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<div class="modal-dialog {% block modal_class %}{% endblock %}"> <div class="modal-dialog {% block modal_class %}{% endblock %}">
<div class="modal-content animated fadeIn"> <div class="modal-content animated fadeIn">
<div class="modal-header"> <div class="modal-header">
<button data-dismiss="modal" class="close close_btn1" type="button"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> <button data-dismiss="modal" id="close_button1" class="close close_btn1 " type="button"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
<h4 class="modal-title">{% block modal_title %}{% endblock %}</h4> <h4 class="modal-title">{% block modal_title %}{% endblock %}</h4>
<small>{% block modal_comment %}{% endblock %}</small> <small>{% block modal_comment %}{% endblock %}</small>
</div> </div>
...@@ -19,10 +19,32 @@ ...@@ -19,10 +19,32 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
{% block modal_button %} {% block modal_button %}
<button data-dismiss="modal" class="btn btn-white close_btn2" type="button">{% trans "Close" %}</button> <button id="close_button2" data-dismiss="modal" class="btn btn-white close_btn2" type="button">{% trans "Close" %}</button>
<button class="btn btn-primary" type="button" id="{% block modal_confirm_id %}{% endblock %}">{% trans 'Confirm' %}</button> <button class="btn btn-primary" type="button" id="{% block modal_confirm_id %}{% endblock %}">{% trans 'Confirm' %}</button>
{% endblock %} {% endblock %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script>
$(document).ready(function(){
})
.on('click', '#close_button1', function () {
SetMessageLabelEmpty()
})
.on('click', '#close_button2', function () {
SetMessageLabelEmpty()
});
function SetMessageLabelEmpty() {
$('#success_created').html('');
$('#success_created_detail').html('');
$('#created_failed').html('');
$('#created_failed_detail').html('');
$('#success_updated').html('');
$('#success_updated_detail').html('');
$('#updated_failed').html('');
$('#updated_failed_detail').html('');
}
</script>
{% extends '_modal.html' %}
{% load i18n %}
{% block modal_id %}update_modal{% endblock %}
{% block modal_confirm_id %}btn_update_confirm{% endblock %}
{% block modal_body %}
<form method="post" id="fm_import">
{% csrf_token %}
<div class="form-group">
<label class="control-label">{% trans "Download the update template or use the exported CSV file format" %}</label>
<a id="download_update_template" style="display: block">{% trans 'Download the update template' %}</a>
</div>
<div class="form-group">
<label class="control-label" for="update_file">{% trans "Select the CSV file to import" %}</label>
<input id="update_file" type="file" name="file" />
</div>
</form>
<div>
<p class="text-warning" id="success_updated"></p>
<p id="success_updated_detail"></p>
<p class="text-danger" id="updated_failed"></p>
<p id="updated_failed_detail"></p>
</div>
{% endblock %}
...@@ -9,13 +9,13 @@ from ..serializers import UserGroupSerializer, \ ...@@ -9,13 +9,13 @@ from ..serializers import UserGroupSerializer, \
UserGroupUpdateMemberSerializer UserGroupUpdateMemberSerializer
from ..models import UserGroup from ..models import UserGroup
from common.permissions import IsOrgAdmin from common.permissions import IsOrgAdmin
from common.mixins import IDInFilterMixin from common.mixins import IDInCacheFilterMixin
__all__ = ['UserGroupViewSet', 'UserGroupUpdateUserApi'] __all__ = ['UserGroupViewSet', 'UserGroupUpdateUserApi']
class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet): class UserGroupViewSet(IDInCacheFilterMixin, BulkModelViewSet):
filter_fields = ("name",) filter_fields = ("name",)
search_fields = filter_fields search_fields = filter_fields
queryset = UserGroup.objects.all() queryset = UserGroup.objects.all()
......
...@@ -15,7 +15,7 @@ from rest_framework.pagination import LimitOffsetPagination ...@@ -15,7 +15,7 @@ from rest_framework.pagination import LimitOffsetPagination
from common.permissions import ( from common.permissions import (
IsOrgAdmin, IsCurrentUserOrReadOnly, IsOrgAdminOrAppUser IsOrgAdmin, IsCurrentUserOrReadOnly, IsOrgAdminOrAppUser
) )
from common.mixins import IDInFilterMixin from common.mixins import IDInCacheFilterMixin
from common.utils import get_logger from common.utils import get_logger
from orgs.utils import current_org from orgs.utils import current_org
from ..serializers import UserSerializer, UserPKUpdateSerializer, \ from ..serializers import UserSerializer, UserPKUpdateSerializer, \
...@@ -32,7 +32,7 @@ __all__ = [ ...@@ -32,7 +32,7 @@ __all__ = [
] ]
class UserViewSet(IDInFilterMixin, BulkModelViewSet): class UserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
filter_fields = ('username', 'email', 'name', 'id') filter_fields = ('username', 'email', 'name', 'id')
search_fields = filter_fields search_fields = filter_fields
queryset = User.objects.exclude(role=User.ROLE_APP) queryset = User.objects.exclude(role=User.ROLE_APP)
...@@ -40,9 +40,15 @@ class UserViewSet(IDInFilterMixin, BulkModelViewSet): ...@@ -40,9 +40,15 @@ class UserViewSet(IDInFilterMixin, BulkModelViewSet):
permission_classes = (IsOrgAdmin,) permission_classes = (IsOrgAdmin,)
pagination_class = LimitOffsetPagination pagination_class = LimitOffsetPagination
def send_created_signal(self, users):
if not isinstance(users, list):
users = [users]
for user in users:
post_user_create.send(self.__class__, user=user)
def perform_create(self, serializer): def perform_create(self, serializer):
user = serializer.save() users = serializer.save()
post_user_create.send(self.__class__, user=user) self.send_created_signal(users)
def get_queryset(self): def get_queryset(self):
queryset = current_org.get_org_users() queryset = current_org.get_org_users()
...@@ -213,4 +219,4 @@ class UserResetOTPApi(generics.RetrieveAPIView): ...@@ -213,4 +219,4 @@ class UserResetOTPApi(generics.RetrieveAPIView):
user.otp_secret_key = '' user.otp_secret_key = ''
user.save() user.save()
logout(request) logout(request)
return Response({"msg": "success"}) return Response({"msg": "success"})
\ No newline at end of file
...@@ -19,12 +19,21 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer): ...@@ -19,12 +19,21 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer):
list_serializer_class = AdaptedBulkListSerializer list_serializer_class = AdaptedBulkListSerializer
fields = [ fields = [
'id', 'name', 'username', 'email', 'groups', 'groups_display', 'id', 'name', 'username', 'email', 'groups', 'groups_display',
'role', 'role_display', 'avatar_url', 'wechat', 'phone', 'role', 'role_display', 'wechat', 'phone', 'otp_level',
'otp_level', 'comment', 'source', 'source_display', 'comment', 'source', 'source_display', 'is_valid', 'is_expired',
'is_valid', 'is_expired', 'is_active', 'is_active', 'created_by', 'is_first_login',
'created_by', 'is_first_login', 'date_password_last_updated', 'date_expired', 'avatar_url',
'date_password_last_updated', 'date_expired',
] ]
extra_kwargs = {
'groups_display': {'label': _('Groups name')},
'source_display': {'label': _('Source name')},
'is_first_login': {'label': _('Is first login'), 'read_only': True},
'role_display': {'label': _('Role name')},
'is_valid': {'label': _('Is valid')},
'is_expired': {'label': _('Is expired')},
'avatar_url': {'label': _('Avatar url')},
'created_by': {'read_only': True}, 'source': {'read_only': True}
}
class UserPKUpdateSerializer(serializers.ModelSerializer): class UserPKUpdateSerializer(serializers.ModelSerializer):
...@@ -48,17 +57,20 @@ class UserUpdateGroupSerializer(serializers.ModelSerializer): ...@@ -48,17 +57,20 @@ class UserUpdateGroupSerializer(serializers.ModelSerializer):
class UserGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer): class UserGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer):
users = serializers.SerializerMethodField() users = serializers.PrimaryKeyRelatedField(
required=False, many=True, queryset=User.objects.all(), label=_('User')
)
class Meta: class Meta:
model = UserGroup model = UserGroup
list_serializer_class = AdaptedBulkListSerializer list_serializer_class = AdaptedBulkListSerializer
fields = '__all__' fields = [
read_only_fields = ['created_by'] 'id', 'org_id', 'name', 'users', 'comment', 'date_created',
'created_by',
@staticmethod ]
def get_users(obj): extra_kwargs = {
return [user.name for user in obj.users.all()] 'created_by': {'label': _('Created by'), 'read_only': True}
}
class UserGroupUpdateMemberSerializer(serializers.ModelSerializer): class UserGroupUpdateMemberSerializer(serializers.ModelSerializer):
......
...@@ -28,4 +28,3 @@ def on_user_create(sender, user=None, **kwargs): ...@@ -28,4 +28,3 @@ def on_user_create(sender, user=None, **kwargs):
logger.info(" - Sending welcome mail ...".format(user.name)) logger.info(" - Sending welcome mail ...".format(user.name))
if user.email: if user.email:
send_user_created_mail(user) send_user_created_mail(user)
{% extends '_import_modal.html' %}
{% load i18n %}
{% block modal_title%}{% trans "Import user groups" %}{% endblock %}
{% block import_modal_download_template_url %}{% url "api-users:user-group-list" %}{% endblock %}
\ No newline at end of file
{% extends '_update_modal.html' %}
{% load i18n %}
{% block modal_title%}{% trans "Update user group" %}{% endblock %}
\ No newline at end of file
{% extends '_modal.html' %} {% extends '_import_modal.html' %}
{% load i18n %} {% load i18n %}
{% block modal_id %}user_import_modal{% endblock %}
{% block modal_title%}{% trans "Import user" %}{% endblock %} {% block modal_title%}{% trans "Import users" %}{% endblock %}
{% block modal_body %}
<p class="text-success">{% trans "Download template or use export csv format" %}</p> {% block import_modal_download_template_url %}{% url "api-users:user-list" %}{% endblock %}
<form method="post" action="{% url 'users:user-import' %}" id="fm_user_import" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<label class="control-label" for="id_users">{% trans "Template" %}</label>
<a href="{% url 'users:user-export' %}" style="display: block">{% trans 'Download' %}</a>
</div>
<div class="form-group">
<label class="control-label" for="id_users">{% trans "Users csv file" %}</label>
<input id="id_users" type="file" name="file" />
<span class="help-block red-fonts">{% trans 'If set id, will use this id update user existed' %}</span>
</div>
</form>
<p>
<p class="text-success" id="id_created"></p>
<p id="id_created_detail"></p>
<p class="text-warning" id="id_updated"></p>
<p id="id_updated_detail"></p>
<p class="text-danger" id="id_failed"></p>
<p id="id_failed_detail"></p>
</p>
{% endblock %}
{% block modal_confirm_id %}btn_user_import{% endblock %}
{% extends '_update_modal.html' %}
{% load i18n %}
{% block modal_title%}{% trans "Update user" %}{% endblock %}
\ No newline at end of file
{% extends '_base_list.html' %} {% extends '_base_list.html' %}
{% load i18n static %} {% load i18n static %}
{% block table_search %}{% endblock %} {% block table_search %}
<div class="" style="float: right">
<div class=" btn-group">
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">CSV <span class="caret"></span></button>
<ul class="dropdown-menu">
<li>
<a class=" btn_export" tabindex="0">
<span>{% trans "Export" %}</span>
</a>
</li>
<li>
<a class=" btn_import" data-toggle="modal" data-target="#import_modal" tabindex="0">
<span>{% trans "Import" %}</span>
</a>
</li>
<li>
<a class=" btn_update" data-toggle="modal" data-target="#update_modal" tabindex="0">
<span>{% trans "Update" %}</span>
</a>
</li>
</ul>
</div>
</div>
{% endblock %}
{% block table_container %} {% block table_container %}
<div class="pull-left m-r-5"><a href="{% url 'users:user-group-create' %}" class="btn btn-sm btn-primary ">{% trans "Create user group" %}</a></div> <div class="pull-left m-r-5"><a href="{% url 'users:user-group-create' %}" class="btn btn-sm btn-primary ">{% trans "Create user group" %}</a></div>
<table class="table table-striped table-bordered table-hover " id="group_list_table" > <table class="table table-striped table-bordered table-hover " id="group_list_table" >
...@@ -16,7 +39,8 @@ ...@@ -16,7 +39,8 @@
</tr> </tr>
</thead> </thead>
</table> </table>
{% include "users/_user_groups_import_modal.html" %}
{% include "users/_user_groups_update_modal.html" %}
{% endblock %} {% endblock %}
{% block content_bottom_left %}{% endblock %} {% block content_bottom_left %}{% endblock %}
...@@ -111,6 +135,78 @@ $(document).ready(function() { ...@@ -111,6 +135,78 @@ $(document).ready(function() {
default: default:
break; break;
} }
}).on('click', '.btn_export', function(){
var data_table = $('#group_list_table').DataTable();
var rows = data_table.rows('.selected').data();
var groups = [];
$.each(rows, function (index, obj) {
groups.push(obj.id)
});
var data = {
'resources': groups
};
var search = $("input[type='search']").val();
var props = {
method: "POST",
body: JSON.stringify(data),
success_url: "{% url 'api-users:user-group-list' %}",
format: "csv",
params: {
search: search
}
};
APIExportData(props);
}).on('click', '#btn_import_confirm',function () {
var url = "{% url 'api-users:user-group-list' %}";
var file = document.getElementById('id_file').files[0];
if(!file){
toastr.error("{% trans "Please select file" %}");
return
}
var data_table = $('#group_list_table').DataTable();
APIImportData({
url: url,
method: "POST",
body: file,
data_table: data_table
});
}) })
.on('click', '#download_update_template', function(){
var data_table = $('#group_list_table').DataTable();
var rows = data_table.rows('.selected').data();
var groups = [];
$.each(rows, function (index, obj) {
groups.push(obj.id)
});
var data = {
'resources': groups
};
var search = $("input[type='search']").val();
var props = {
method: "POST",
body: JSON.stringify(data),
success_url: "{% url 'api-users:user-group-list' %}?format=csv&template=update",
format: "csv",
params: {
search: search
}
};
APIExportData(props);
}).on('click', '#btn_update_confirm',function () {
var url = "{% url 'api-users:user-group-list' %}";
var file = document.getElementById('update_file').files[0];
if(!file){
toastr.error("{% trans "Please select file" %}");
return
}
var data_table = $('#group_list_table').DataTable();
APIImportData({
url: url,
method: "PUT",
body: file,
data_table: data_table
});
})
</script> </script>
{% endblock %} {% endblock %}
{% extends '_base_list.html' %} {% extends '_base_list.html' %}
{% load i18n static %} {% load i18n static %}
{% block table_search %} {% block table_search %}
<div class="html5buttons"> <div class="" style="float: right">
<div class="dt-buttons btn-group"> <div class=" btn-group">
<a class="btn btn-default btn_import" data-toggle="modal" data-target="#user_import_modal" tabindex="0"> <button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">CSV <span class="caret"></span></button>
<span>{% trans "Import" %}</span> <ul class="dropdown-menu">
</a> <li>
<a class="btn btn-default btn_export" tabindex="0"> <a class=" btn_export" tabindex="0">
<span>{% trans "Export" %}</span> <span>{% trans "Export" %}</span>
</a> </a>
</div> </li>
</div> <li>
<a class=" btn_import" data-toggle="modal" data-target="#import_modal" tabindex="0">
<span>{% trans "Import" %}</span>
</a>
</li>
<li>
<a class=" btn_update" data-toggle="modal" data-target="#update_modal" tabindex="0">
<span>{% trans "Update" %}</span>
</a>
</li>
</ul>
</div>
</div>
{% endblock %} {% endblock %}
{% block table_container %} {% block table_container %}
<div class="uc pull-left m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create user" %} </a></div> <div class="uc pull-left m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create user" %} </a></div>
...@@ -48,6 +60,7 @@ ...@@ -48,6 +60,7 @@
</div> </div>
</div> </div>
{% include "users/_user_import_modal.html" %} {% include "users/_user_import_modal.html" %}
{% include "users/_user_update_modal.html" %}
{% endblock %} {% endblock %}
{% block content_bottom_left %}{% endblock %} {% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
...@@ -113,6 +126,7 @@ function initTable() { ...@@ -113,6 +126,7 @@ function initTable() {
return table return table
} }
$(document).ready(function(){ $(document).ready(function(){
var table = initTable(); var table = initTable();
var fields = $('#fm_user_bulk_update .form-group'); var fields = $('#fm_user_bulk_update .form-group');
...@@ -120,87 +134,127 @@ $(document).ready(function(){ ...@@ -120,87 +134,127 @@ $(document).ready(function(){
console.log(value) console.log(value)
}); });
$('.btn_export').click(function () { $('.btn_export').click(function () {
var users = [];
var rows = table.rows('.selected').data(); var rows = table.rows('.selected').data();
if(rows.length===0){ var users = [];
rows = table.rows().data();
}
$.each(rows, function (index, obj) { $.each(rows, function (index, obj) {
users.push(obj.id) users.push(obj.id)
}); });
$.ajax({ var data = {
url: "{% url 'users:user-export' %}", 'resources': users
method: 'POST', };
data: JSON.stringify({users_id: users}), var search = $("input[type='search']").val();
dataType: "json", var props = {
success: function (data, textStatus) { method: "POST",
window.open(data.redirect) body: JSON.stringify(data),
}, success_url: "{% url 'api-users:user-list' %}",
error: function () { format: 'csv',
toastr.error('Export failed'); params: {
search: search
} }
}) };
APIExportData(props);
}); });
$('#btn_user_import').click(function() { $('#btn_import_confirm').click(function() {
var $form = $('#fm_user_import'); var url = "{% url 'api-users:user-list' %}";
$form.find('.help-block').remove(); var file = document.getElementById('id_file').files[0];
function success (data) { if(!file){
if (data.valid === false) { toastr.error("{% trans "Please select file" %}");
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_users')); return
} else { }
$('#id_created').html(data.created_info); var data_table = $('#user_list_table').DataTable();
$('#id_created_detail').html(data.created.join(', ')); APIImportData({
$('#id_updated').html(data.updated_info); url: url,
$('#id_updated_detail').html(data.updated.join(', ')); method: "POST",
$('#id_failed').html(data.failed_info); body: file,
$('#id_failed_detail').html(data.failed.join(', ')); data_table: data_table
var $data_table = $('#user_list_table').DataTable(); });
$data_table.ajax.reload(); });
$('#download_update_template').click(function () {
var rows = table.rows('.selected').data();
var users = [];
$.each(rows, function (index, obj) {
users.push(obj.id)
});
var data = {
'resources': users
};
var search = $("input[type='search']").val();
var props = {
method: "POST",
body: JSON.stringify(data),
success_url: "{% url 'api-users:user-list' %}?format=csv&template=update",
format: 'csv',
params: {
search: search
} }
};
APIExportData(props);
});
$('#btn_update_confirm').click(function() {
var url = "{% url 'api-users:user-list' %}";
var file = document.getElementById('update_file').files[0];
if(!file){
toastr.error("{% trans "Please select file" %}");
return
} }
$form.ajaxSubmit({success: success}); var data_table = $('#user_list_table').DataTable();
}) APIImportData({
url: url,
method: "PUT",
body: file,
data_table: data_table
});
});
}).on('click', '#btn_bulk_update', function(){ }).on('click', '#btn_bulk_update', function(){
var action = $('#slct_bulk_update').val(); var action = $('#slct_bulk_update').val();
var $data_table = $('#user_list_table').DataTable(); var $data_table = $('#user_list_table').DataTable();
var id_list = []; var id_list = [];
var plain_id_list = [];
$data_table.rows({selected: true}).every(function(){ $data_table.rows({selected: true}).every(function(){
id_list.push({pk: this.data().id}); id_list.push(this.data().id);
plain_id_list.push(this.data().id);
}); });
if (id_list === []) { if (id_list.length === 0) {
return false; return false;
} }
var the_url = "{% url 'api-users:user-list' %}"; var the_url = "{% url 'api-users:user-list' %}";
var data = {
'resources': id_list
};
function refreshTag() {
$('#user_list_table').DataTable().ajax.reload()
}
function doDeactive() { function doDeactive() {
var body = $.each(id_list, function(index, user_object) { var data = [];
user_object['is_active'] = false; $.each(id_list, function(index, object_id) {
var obj = {"pk": object_id, "is_active": false};
data.push(obj);
}); });
function success() { function success() {
location.reload(); setTimeout( function () {
window.location.reload();}, 300);
} }
APIUpdateAttr({ APIUpdateAttr({
url: the_url, url: the_url,
method: 'PATCH', method: 'PATCH',
body: JSON.stringify(body), body: JSON.stringify(data),
success: success success: success
}); });
location.reload();
} }
function doActive() { function doActive() {
var body = $.each(id_list, function(index, user_object) { var data = [];
user_object['is_active'] = true; $.each(id_list, function(index, object_id) {
var obj = {"pk": object_id, "is_active": true};
data.push(obj);
}); });
function success() { function success() {
location.reload(); setTimeout( function () {
window.location.reload();}, 300);
} }
APIUpdateAttr({ APIUpdateAttr({
url: the_url, url: the_url,
method: 'PATCH', method: 'PATCH',
body: JSON.stringify(body), body: JSON.stringify(data),
success: success success: success
}); });
} }
...@@ -214,26 +268,49 @@ $(document).ready(function(){ ...@@ -214,26 +268,49 @@ $(document).ready(function(){
confirmButtonColor: "#DD6B55", confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}", confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false closeOnConfirm: false
}, function() { },function () {
var success = function() { function success(data) {
url = setUrlParam(the_url, 'spm', data.spm);
APIUpdateAttr({
url:url,
method:'DELETE',
success:refreshTag,
flash_message:false,
});
var msg = "{% trans 'User Deleted.' %}"; var msg = "{% trans 'User Deleted.' %}";
swal("{% trans 'User Delete' %}", msg, "success"); swal("{% trans 'User Delete' %}", msg, "success");
$('#user_list_table').DataTable().ajax.reload(); }
}; function fail() {
var fail = function() {
var msg = "{% trans 'User Deleting failed.' %}"; var msg = "{% trans 'User Deleting failed.' %}";
swal("{% trans 'User Delete' %}", msg, "error"); swal("{% trans 'User Delete' %}", msg, "error");
}; }
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list); APIUpdateAttr({
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail}); url: "{% url 'api-common:resources-cache' %}",
jumpserver.checked = false; method:'POST',
}); body:JSON.stringify(data),
success:success,
error:fail
})
})
} }
function doUpdate() { function doUpdate() {
var users_id = plain_id_list.join(','); function fail(data) {
var url = "{% url 'users:user-bulk-update' %}?users_id=" + users_id; toastr.error(JSON.parse(data))
location.href = url }
} function success(data) {
var url = "{% url 'users:user-bulk-update' %}";
location.href= setUrlParam(url, 'spm', data.spm);
}
APIUpdateAttr({
url: "{% url 'api-common:resources-cache' %}",
method:'POST',
body:JSON.stringify(data),
flash_message:false,
success:success,
error:fail
})
}
switch(action) { switch(action) {
case 'deactive': case 'deactive':
doDeactive(); doDeactive();
......
...@@ -31,7 +31,9 @@ from django.views.generic.detail import DetailView ...@@ -31,7 +31,9 @@ from django.views.generic.detail import DetailView
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth import logout as auth_logout from django.contrib.auth import logout as auth_logout
from common.const import create_success_msg, update_success_msg from common.const import (
create_success_msg, update_success_msg, KEY_CACHE_RESOURCES_ID
)
from common.mixins import JSONResponseMixin from common.mixins import JSONResponseMixin
from common.utils import get_logger, get_object_or_none, is_uuid, ssh_key_gen from common.utils import get_logger, get_object_or_none, is_uuid, ssh_key_gen
from common.permissions import AdminUserRequiredMixin from common.permissions import AdminUserRequiredMixin
...@@ -156,15 +158,12 @@ class UserBulkUpdateView(AdminUserRequiredMixin, TemplateView): ...@@ -156,15 +158,12 @@ class UserBulkUpdateView(AdminUserRequiredMixin, TemplateView):
id_list = None id_list = None
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
users_id = self.request.GET.get('users_id', '') spm = request.GET.get('spm', '')
self.id_list = [i for i in users_id.split(',')] users_id = cache.get(KEY_CACHE_RESOURCES_ID.format(spm))
if kwargs.get('form'): if kwargs.get('form'):
self.form = kwargs['form'] self.form = kwargs['form']
elif users_id: elif users_id:
self.form = self.form_class( self.form = self.form_class(initial={'users': users_id})
initial={'users': self.id_list}
)
else: else:
self.form = self.form_class() self.form = self.form_class()
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
......
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