Unverified Commit e1919d0a authored by 老广's avatar 老广 Committed by GitHub

Asset meta (#3539)

- 更改了资产表单,影响
  - 资产创建和更新
- 增加了资产平台数据库,影响
  - 平台创建更新和删除
- 更改了资产的platform字段,又一个字符字段,改为一个外键,影响 
  - 资产创建和更新
  - 资产连接 [windows,linux]
  - 测试连接等ansible任务
  - 自动化云导入
- 更改了资产的序列化器,影响
  - 资产创建更新列表
- 统一了树列表基础模板,影响
  - 资产列表页,权限列表页,vault页,资产收集页
- 统一了导入导出组件,影响
  - 资产导入导出
  - 用户导入导出
  - 用户组导入导出
  - 系统用户导入导出
  - 管理用户导入导出
  - vault导出导出
  - 收集用户列表导入导出
- 修改用户更新密码信号,影响
  - 修改用户密码产生的改密日志

- 新增Model instance序列化工具函数,影响
  - 操作日志生成
- 修改api mixin,新增 serializer_classes字段,serializer_classes = {"default": "", "display": "", "list": .., "other_action": ""}, 根据用户请求的方式返回不同的serializer_class,影响

  - 用户的viewset
  - 资产权限的viewset
- 统一系统配置中的tab切换
- 统一没有nav的页面,影响
  - 重置密码
  - 忘记密码
  - 重置中设置密码
  - 独立的message页面
- 修改用户组列表页,不再返还用户组下的用户,仅有数量
- 组织的一些方法变为layzproperty,避免重复计算
- 修改用户组详情页,影响
  - 用户组增加删除用户

  
parent 4ac4b517
...@@ -4,24 +4,27 @@ ...@@ -4,24 +4,27 @@
import random import random
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from rest_framework.generics import RetrieveAPIView
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from common.utils import get_logger, get_object_or_none from common.utils import get_logger, get_object_or_none
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, IsSuperUser
from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins.api import OrgBulkModelViewSet
from orgs.mixins import generics from orgs.mixins import generics
from ..models import Asset, Node from ..models import Asset, Node, Platform
from .. import serializers from .. import serializers
from ..tasks import update_asset_hardware_info_manual, \ from ..tasks import (
test_asset_connectivity_manual update_asset_hardware_info_manual, test_asset_connectivity_manual
)
from ..filters import AssetByNodeFilterBackend, LabelFilterBackend from ..filters import AssetByNodeFilterBackend, LabelFilterBackend
logger = get_logger(__file__) logger = get_logger(__file__)
__all__ = [ __all__ = [
'AssetViewSet', 'AssetViewSet', 'AssetPlatformRetrieveApi',
'AssetRefreshHardwareApi', 'AssetAdminUserTestApi', 'AssetRefreshHardwareApi', 'AssetAdminUserTestApi',
'AssetGatewayApi', 'AssetGatewayApi', 'AssetPlatformViewSet',
] ]
...@@ -53,6 +56,34 @@ class AssetViewSet(OrgBulkModelViewSet): ...@@ -53,6 +56,34 @@ class AssetViewSet(OrgBulkModelViewSet):
self.set_assets_node(assets) self.set_assets_node(assets)
class AssetPlatformRetrieveApi(RetrieveAPIView):
queryset = Platform.objects.all()
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.PlatformSerializer
def get_object(self):
asset_pk = self.kwargs.get('pk')
asset = get_object_or_404(Asset, pk=asset_pk)
return asset.platform
class AssetPlatformViewSet(ModelViewSet):
queryset = Platform.objects.all()
permission_classes = (IsSuperUser,)
serializer_class = serializers.PlatformSerializer
filterset_fields = ['name', 'base']
search_fields = ['name']
def check_object_permissions(self, request, obj):
if request.method.lower() in ['delete', 'put', 'patch'] and \
obj.internal:
self.permission_denied(
request, message={"detail": "Internal platform"}
)
return super().check_object_permissions(request, obj)
class AssetRefreshHardwareApi(generics.RetrieveAPIView): class AssetRefreshHardwareApi(generics.RetrieveAPIView):
""" """
Refresh asset hardware info Refresh asset hardware info
......
...@@ -177,7 +177,7 @@ class NodeChildrenAsTreeApi(NodeChildrenApi): ...@@ -177,7 +177,7 @@ class NodeChildrenAsTreeApi(NodeChildrenApi):
if not include_assets: if not include_assets:
return queryset return queryset
assets = self.instance.get_assets().only( assets = self.instance.get_assets().only(
"id", "hostname", "ip", 'platform', "os", "id", "hostname", "ip", "os",
"org_id", "protocols", "org_id", "protocols",
) )
for asset in assets: for asset in assets:
......
...@@ -5,3 +5,4 @@ from .label import * ...@@ -5,3 +5,4 @@ from .label import *
from .user import * from .user import *
from .domain import * from .domain import *
from .cmd_filter import * from .cmd_filter import *
from .platform import *
...@@ -6,13 +6,13 @@ from django.utils.translation import gettext_lazy as _ ...@@ -6,13 +6,13 @@ from django.utils.translation import gettext_lazy as _
from common.utils import get_logger from common.utils import get_logger
from orgs.mixins.forms import OrgModelForm from orgs.mixins.forms import OrgModelForm
from ..models import Asset, Node from ..models import Asset
from ..const import GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_HELP_TEXT from ..const import GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_HELP_TEXT
logger = get_logger(__file__) logger = get_logger(__file__)
__all__ = [ __all__ = [
'AssetCreateForm', 'AssetUpdateForm', 'AssetBulkUpdateForm', 'ProtocolForm', 'AssetCreateUpdateForm', 'AssetBulkUpdateForm', 'ProtocolForm',
] ]
...@@ -27,17 +27,27 @@ class ProtocolForm(forms.Form): ...@@ -27,17 +27,27 @@ class ProtocolForm(forms.Form):
) )
class AssetCreateForm(OrgModelForm): class AssetCreateUpdateForm(OrgModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if self.data: self.set_platform_to_name()
return self.set_fields_queryset()
def set_fields_queryset(self):
nodes_field = self.fields['nodes'] nodes_field = self.fields['nodes']
nodes_choices = []
if self.instance: if self.instance:
nodes_field.choices = [(n.id, n.full_value) for n in nodes_choices = [
self.instance.nodes.all()] (n.id, n.full_value) for n in
else: self.instance.nodes.all()
nodes_field.choices = [] ]
nodes_field.choices = nodes_choices
def set_platform_to_name(self):
platform_field = self.fields['platform']
platform_field.to_field_name = 'name'
if self.instance:
self.initial['platform'] = self.instance.platform.name
def add_nodes_initial(self, node): def add_nodes_initial(self, node):
nodes_field = self.fields['nodes'] nodes_field = self.fields['nodes']
...@@ -49,7 +59,7 @@ class AssetCreateForm(OrgModelForm): ...@@ -49,7 +59,7 @@ class AssetCreateForm(OrgModelForm):
fields = [ fields = [
'hostname', 'ip', 'public_ip', 'protocols', 'comment', 'hostname', 'ip', 'public_ip', 'protocols', 'comment',
'nodes', 'is_active', 'admin_user', 'labels', 'platform', 'nodes', 'is_active', 'admin_user', 'labels', 'platform',
'domain', 'domain', 'number',
] ]
widgets = { widgets = {
'nodes': forms.SelectMultiple(attrs={ 'nodes': forms.SelectMultiple(attrs={
...@@ -64,52 +74,8 @@ class AssetCreateForm(OrgModelForm): ...@@ -64,52 +74,8 @@ class AssetCreateForm(OrgModelForm):
'domain': forms.Select(attrs={ 'domain': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Domain') 'class': 'select2', 'data-placeholder': _('Domain')
}), }),
} 'platform': forms.Select(attrs={
labels = { 'class': 'select2', 'data-placeholder': _('Platform')
'nodes': _("Node"),
}
help_texts = {
'hostname': GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_HELP_TEXT,
'admin_user': _(
'root or other NOPASSWD sudo privilege user existed in asset,'
'If asset is windows or other set any one, more see admin user left menu'
),
'platform': _("Windows 2016 RDP protocol is different, If is window 2016, set it"),
'domain': _("If your have some network not connect with each other, you can set domain")
}
class AssetUpdateForm(OrgModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.data:
return
nodes_field = self.fields['nodes']
if self.instance:
nodes_field.choices = ((n.id, n.full_value) for n in
self.instance.nodes.all())
else:
nodes_field.choices = []
class Meta:
model = Asset
fields = [
'hostname', 'ip', 'protocols', 'nodes', 'is_active', 'platform',
'public_ip', 'number', 'comment', 'admin_user', 'labels',
'domain',
]
widgets = {
'nodes': forms.SelectMultiple(attrs={
'class': 'nodes-select2', 'data-placeholder': _('Node')
}),
'admin_user': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Admin user')
}),
'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Label')
}),
'domain': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Domain')
}), }),
} }
labels = { labels = {
......
# -*- coding: utf-8 -*-
from django import forms
from django.utils.translation import ugettext_lazy as _
from ..models import Platform
__all__ = ['PlatformForm', 'PlatformMetaForm']
class PlatformMetaForm(forms.Form):
SECURITY_CHOICES = (
('rdp', "RDP"),
('nla', "NLA"),
('tls', 'TLS'),
('any', "Any"),
)
CONSOLE_CHOICES = (
(True, _('Yes')),
(False, _('No')),
)
security = forms.ChoiceField(
choices=SECURITY_CHOICES, initial='any', label=_("RDP security"),
required=False,
)
console = forms.ChoiceField(
choices=CONSOLE_CHOICES, initial=False, label=_("RDP console"),
required=False,
)
class PlatformForm(forms.ModelForm):
class Meta:
model = Platform
fields = [
'name', 'base', 'comment',
]
labels = {
'base': _("Base platform")
}
# Generated by Django 2.2.7 on 2019-12-06 07:26
import common.fields.model
from django.db import migrations, models
def create_internal_platform(apps, schema_editor):
model = apps.get_model("assets", "Platform")
db_alias = schema_editor.connection.alias
type_platforms = (
('Linux', 'Linux', None),
('Unix', 'Unix', None),
('MacOS', 'MacOS', None),
('BSD', 'BSD', None),
('Windows', 'Windows', None),
('Windows2016', 'Windows', {'security': 'tls'}),
('Other', 'Other', None),
)
for name, base, meta in type_platforms:
model.objects.using(db_alias).create(
name=name, base=base, internal=True, meta=meta
)
class Migration(migrations.Migration):
dependencies = [
('assets', '0043_auto_20191114_1111'),
]
operations = [
migrations.CreateModel(
name='Platform',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.SlugField(allow_unicode=True, unique=True, verbose_name='Name')),
('base', models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Other', 'Other')], default='Linux', max_length=16, verbose_name='Base')),
('charset', models.CharField(choices=[('utf8', 'UTF-8'), ('gbk', 'GBK')], default='utf8', max_length=8, verbose_name='Charset')),
('meta', common.fields.model.JsonDictTextField(blank=True, null=True, verbose_name='Meta')),
('internal', models.BooleanField(default=False, verbose_name='Internal')),
('comment', models.TextField(blank=True, null=True, verbose_name='Comment')),
],
),
migrations.RunPython(create_internal_platform)
]
# Generated by Django 2.2.7 on 2019-12-06 08:07
import assets.models.asset
from django.db import migrations, models
import django.db.models.deletion
def migrate_platform_to_asset_type(apps, schema_editor):
asset_model = apps.get_model("assets", "Asset")
platform_model = apps.get_model("assets", "Platform")
db_alias = schema_editor.connection.alias
platforms = platform_model.objects.using(db_alias).all()
platforms_map = {p.name: p for p in platforms}
for name, p in platforms_map.items():
asset_model.objects.using(db_alias)\
.filter(_platform=name)\
.update(platform=p)
class Migration(migrations.Migration):
dependencies = [
('assets', '0044_platform'),
]
operations = [
migrations.RenameField(
model_name='asset',
old_name='platform',
new_name='_platform',
),
migrations.AddField(
model_name='asset',
name='platform',
field=models.ForeignKey(
default=assets.models.asset.Platform.default,
on_delete=django.db.models.deletion.PROTECT,
related_name='assets', to='assets.Platform',
verbose_name='Platform'),
),
migrations.RunPython(migrate_platform_to_asset_type),
migrations.RemoveField(
model_name='asset',
name='_platform',
),
]
...@@ -11,10 +11,12 @@ from collections import OrderedDict ...@@ -11,10 +11,12 @@ from collections import OrderedDict
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from .utils import Connectivity from common.fields.model import JsonDictTextField
from common.utils import lazyproperty
from orgs.mixins.models import OrgModelMixin, OrgManager from orgs.mixins.models import OrgModelMixin, OrgManager
from .utils import Connectivity
__all__ = ['Asset', 'ProtocolsMixin'] __all__ = ['Asset', 'ProtocolsMixin', 'Platform']
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -37,6 +39,13 @@ def default_node(): ...@@ -37,6 +39,13 @@ def default_node():
return None return None
class AssetManager(OrgManager):
def get_queryset(self):
return super().get_queryset().annotate(
platform_base=models.F('platform__base')
)
class AssetQuerySet(models.QuerySet): class AssetQuerySet(models.QuerySet):
def active(self): def active(self):
return self.filter(is_active=True) return self.filter(is_active=True)
...@@ -119,6 +128,41 @@ class NodesRelationMixin: ...@@ -119,6 +128,41 @@ class NodesRelationMixin:
return nodes return nodes
class Platform(models.Model):
CHARSET_CHOICES = (
('utf8', 'UTF-8'),
('gbk', 'GBK'),
)
BASE_CHOICES = (
('Linux', 'Linux'),
('Unix', 'Unix'),
('MacOS', 'MacOS'),
('BSD', 'BSD'),
('Windows', 'Windows'),
('Other', 'Other'),
)
name = models.SlugField(verbose_name=_("Name"), unique=True, allow_unicode=True)
base = models.CharField(choices=BASE_CHOICES, max_length=16, default='Linux', verbose_name=_("Base"))
charset = models.CharField(default='utf8', choices=CHARSET_CHOICES, max_length=8, verbose_name=_("Charset"))
meta = JsonDictTextField(blank=True, null=True, verbose_name=_("Meta"))
internal = models.BooleanField(default=False, verbose_name=_("Internal"))
comment = models.TextField(blank=True, null=True, verbose_name=_("Comment"))
@classmethod
def default(cls):
linux, created = cls.objects.get_or_create(
defaults={'name': 'Linux'}, name='Linux'
)
return linux.id
def __str__(self):
return self.name
class Meta:
verbose_name = _("Platform")
# ordering = ('name',)
class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin): class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
# Important # Important
PLATFORM_CHOICES = ( PLATFORM_CHOICES = (
...@@ -138,9 +182,8 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin): ...@@ -138,9 +182,8 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
choices=ProtocolsMixin.PROTOCOL_CHOICES, choices=ProtocolsMixin.PROTOCOL_CHOICES,
verbose_name=_('Protocol')) verbose_name=_('Protocol'))
port = models.IntegerField(default=22, verbose_name=_('Port')) port = models.IntegerField(default=22, verbose_name=_('Port'))
protocols = models.CharField(max_length=128, default='ssh/22', blank=True, verbose_name=_("Protocols")) protocols = models.CharField(max_length=128, default='ssh/22', blank=True, verbose_name=_("Protocols"))
platform = models.CharField(max_length=128, choices=PLATFORM_CHOICES, default='Linux', verbose_name=_('Platform')) platform = models.ForeignKey(Platform, default=Platform.default, on_delete=models.PROTECT, verbose_name=_("Platform"), related_name='assets')
domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', verbose_name=_("Domain"), on_delete=models.SET_NULL) domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', verbose_name=_("Domain"), on_delete=models.SET_NULL)
nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes")) nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes"))
is_active = models.BooleanField(default=True, verbose_name=_('Is active')) is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
...@@ -175,7 +218,7 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin): ...@@ -175,7 +218,7 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created')) date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created'))
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment')) comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
objects = OrgManager.from_queryset(AssetQuerySet)() objects = AssetManager.from_queryset(AssetQuerySet)()
_connectivity = None _connectivity = None
def __str__(self): def __str__(self):
...@@ -191,19 +234,20 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin): ...@@ -191,19 +234,20 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
return True, warning return True, warning
def is_windows(self): def is_windows(self):
if self.platform in ("Windows", "Windows2016"): return self.platform_base == "Windows"
return True
else:
return False
def is_unixlike(self): def is_unixlike(self):
if self.platform not in ("Windows", "Windows2016", "Other"): if self.platform_base not in ("Windows", "Windows2016", "Other"):
return True return True
else: else:
return False return False
def is_support_ansible(self): def is_support_ansible(self):
return self.has_protocol('ssh') and self.platform not in ("Other",) return self.has_protocol('ssh') and self.platform_base not in ("Other",)
@lazyproperty
def platform_base(self):
return self.platform.base
@property @property
def cpu_info(self): def cpu_info(self):
...@@ -264,9 +308,9 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin): ...@@ -264,9 +308,9 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
def as_tree_node(self, parent_node): def as_tree_node(self, parent_node):
from common.tree import TreeNode from common.tree import TreeNode
icon_skin = 'file' icon_skin = 'file'
if self.platform.lower() == 'windows': if self.platform_base.lower() == 'windows':
icon_skin = 'windows' icon_skin = 'windows'
elif self.platform.lower() == 'linux': elif self.platform_base.lower() == 'linux':
icon_skin = 'linux' icon_skin = 'linux'
data = { data = {
'id': str(self.id), 'id': str(self.id),
...@@ -283,7 +327,7 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin): ...@@ -283,7 +327,7 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
'hostname': self.hostname, 'hostname': self.hostname,
'ip': self.ip, 'ip': self.ip,
'protocols': self.protocols_as_list, 'protocols': self.protocols_as_list,
'platform': self.platform, 'platform': self.platform_base,
} }
} }
} }
......
...@@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _
from orgs.mixins.serializers import BulkOrgResourceModelSerializer from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from common.serializers import AdaptedBulkListSerializer from common.serializers import AdaptedBulkListSerializer
from ..models import Asset, Node, Label from ..models import Asset, Node, Label, Platform
from ..const import ( from ..const import (
GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_PATTERN, GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_PATTERN,
GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_ERROR_MSG GENERAL_FORBIDDEN_SPECIAL_CHARACTERS_ERROR_MSG
...@@ -16,7 +16,8 @@ from .base import ConnectivitySerializer ...@@ -16,7 +16,8 @@ from .base import ConnectivitySerializer
__all__ = [ __all__ = [
'AssetSerializer', 'AssetSimpleSerializer', 'AssetSerializer', 'AssetSimpleSerializer',
'ProtocolsField', 'ProtocolsField', 'PlatformSerializer',
'AssetDetailSerializer',
] ]
...@@ -65,6 +66,9 @@ class ProtocolsField(serializers.ListField): ...@@ -65,6 +66,9 @@ class ProtocolsField(serializers.ListField):
class AssetSerializer(BulkOrgResourceModelSerializer): class AssetSerializer(BulkOrgResourceModelSerializer):
platform = serializers.SlugRelatedField(
slug_field='name', queryset=Platform.objects.all(), label=_("Platform")
)
protocols = ProtocolsField(label=_('Protocols'), required=False) protocols = ProtocolsField(label=_('Protocols'), required=False)
connectivity = ConnectivitySerializer(read_only=True, label=_("Connectivity")) connectivity = ConnectivitySerializer(read_only=True, label=_("Connectivity"))
...@@ -111,7 +115,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer): ...@@ -111,7 +115,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
queryset = queryset.prefetch_related( queryset = queryset.prefetch_related(
Prefetch('nodes', queryset=Node.objects.all().only('id')), Prefetch('nodes', queryset=Node.objects.all().only('id')),
Prefetch('labels', queryset=Label.objects.all().only('id')), Prefetch('labels', queryset=Label.objects.all().only('id')),
).select_related('admin_user', 'domain') ).select_related('admin_user', 'domain', 'platform')
return queryset return queryset
def compatible_with_old_protocol(self, validated_data): def compatible_with_old_protocol(self, validated_data):
...@@ -139,6 +143,21 @@ class AssetSerializer(BulkOrgResourceModelSerializer): ...@@ -139,6 +143,21 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
return super().update(instance, validated_data) return super().update(instance, validated_data)
class PlatformSerializer(serializers.ModelSerializer):
meta = serializers.DictField(required=False, allow_null=True)
class Meta:
model = Platform
fields = [
'id', 'name', 'base', 'charset',
'internal', 'meta', 'comment'
]
class AssetDetailSerializer(AssetSerializer):
platform = PlatformSerializer(read_only=True)
class AssetSimpleSerializer(serializers.ModelSerializer): class AssetSimpleSerializer(serializers.ModelSerializer):
connectivity = ConnectivitySerializer(read_only=True, label=_("Connectivity")) connectivity = ConnectivitySerializer(read_only=True, label=_("Connectivity"))
......
{% 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 '_import_modal.html' %}
{% load i18n %}
{% block modal_title%}{% trans "Import assets" %}{% endblock %}
{% block import_modal_download_template_url %}{% url "api-assets:asset-list" %}{% endblock %}
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
<div class="wrapper wrapper-content"> <div class="wrapper wrapper-content">
<div class="row"> <div class="row">
<div class="col-lg-3" id="split-left" style="padding-left: 3px;overflow: auto;max-height: 500px"> <div class="col-sm-3" id="split-left" style="padding-left: 3px;overflow: auto;max-height: 500px">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="ibox-content mailbox-content" style="padding-top: 0;padding-left: 1px"> <div class="ibox-content mailbox-content" style="padding-top: 0;padding-left: 1px">
<div class="file-manager "> <div class="file-manager ">
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-9 animated fadeInRight" id="split-right"> <div class="col-sm-9 animated fadeInRight" id="split-right">
<div class="mail-box-header"> <div class="mail-box-header">
<table class="table table-striped table-bordered table-hover " id="asset_list_modal_table" style="width: 100%"> <table class="table table-striped table-bordered table-hover " id="asset_list_modal_table" style="width: 100%">
<thead> <thead>
......
{% 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
...@@ -5,28 +5,7 @@ ...@@ -5,28 +5,7 @@
{% trans 'Jumpserver users of the system using the user to `push system user`, `get assets hardware information`, etc. '%} {% trans 'Jumpserver users of the system using the user to `push system user`, `get assets hardware information`, etc. '%}
{% endblock %} {% endblock %}
{% block table_search %} {% block table_search %}
<div class="" style="float: right"> {% include '_csv_import_export.html' %}
<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 %}
...@@ -42,9 +21,6 @@ ...@@ -42,9 +21,6 @@
<th class="text-center">{% trans 'Name' %}</th> <th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Username' %}</th> <th class="text-center">{% trans 'Username' %}</th>
<th class="text-center">{% trans 'Asset' %}</th> <th class="text-center">{% trans 'Asset' %}</th>
{# <th class="text-center">{% trans 'Reachable' %}</th>#}
{# <th class="text-center">{% trans 'Unreachable' %}</th>#}
{# <th class="text-center">{% trans 'Ratio' %}</th>#}
<th class="text-center">{% trans 'Comment' %}</th> <th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th> <th class="text-center">{% trans 'Action' %}</th>
</tr> </tr>
...@@ -52,8 +28,6 @@ ...@@ -52,8 +28,6 @@
<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 %}
...@@ -80,14 +54,13 @@ function initTable() { ...@@ -80,14 +54,13 @@ function initTable() {
{data: "comment"}, {data: "id", orderable: false, width: "100px"} {data: "comment"}, {data: "id", orderable: false, width: "100px"}
] ]
}; };
admin_user_table = jumpserver.initServerSideDataTable(options); return jumpserver.initServerSideDataTable(options);
return admin_user_table
} }
$(document).ready(function(){ $(document).ready(function(){
initTable(); admin_user_table = initTable();
initCsvImportExport(admin_user_table, "{% trans "Admin user" %}")
}) })
.on('click', '.btn_admin_user_delete', function () { .on('click', '.btn_admin_user_delete', function () {
var $this = $(this); var $this = $(this);
var $data_table = $("#admin_user_list_table").DataTable(); var $data_table = $("#admin_user_list_table").DataTable();
...@@ -100,69 +73,5 @@ $(document).ready(function(){ ...@@ -100,69 +73,5 @@ $(document).ready(function(){
}, 3000); }, 3000);
}) })
.on('click', '.btn_export', function(){
var admin_users = admin_user_table.selected;
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 admin_users = admin_user_table.selected;
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 %}
{% extends 'base.html' %} {% extends '_base_asset_tree_list.html' %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% block help_message %} {% block help_message %}
{# <div class="alert alert-info help-message">#}
{# <button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>#}
{# 左侧是资产树,右击可以新建、删除、更改树节点,授权资产也是以节点方式组织的,右侧是属于该节点下的资产#}
{% trans 'The left side is the asset tree, right click to create, delete, and change the tree node, authorization asset is also organized as a node, and the right side is the asset under that node' %} {% trans 'The left side is the asset tree, right click to create, delete, and change the tree node, authorization asset is also organized as a node, and the right side is the asset under that node' %}
{# </div>#}
{% endblock %} {% endblock %}
{% block custom_head_css_js %} {% block table_container %}
{# <link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">#} <div class="uc pull-left m-r-5"><a class="btn btn-sm btn-primary btn-create-asset"> {% trans "Create asset" %} </a></div>
{# <script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>#} {% include '_csv_import_export.html' %}
<script src="{% static 'js/jquery.form.min.js' %}"></script> <div class="btn-group" style="float: right">
<style type="text/css"> <button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>
div#rMenu { <ul class="dropdown-menu labels">
position:absolute; {% for label in labels %}
visibility:hidden; <li><a style="font-weight: bolder">{{ label.name }}#{{ label.value }}</a></li>
text-align: left; {% endfor %}
{#top: 100%;#} </ul>
top: 0; </div>
left: 0; <table class="table table-striped table-bordered table-hover " id="asset_list_table" style="width: 100%">
z-index: 1000; <thead>
{#float: left;#} <tr>
padding: 0 0; <th class="text-center"><input type="checkbox" class="ipt_check_all"></th>
margin: 2px 0 0; <th class="text-center">{% trans 'Hostname' %}</th>
list-style: none; <th class="text-center">{% trans 'IP' %}</th>
background-clip: padding-box; <th class="text-center">{% trans 'Hardware' %}</th>
} <th class="text-center">{% trans 'Reachable' %}</th>
.dataTables_wrapper .dataTables_processing { <th class="text-center">{% trans 'Action' %}</th>
opacity: .9; </tr>
border: none; </thead>
} <tbody>
div#rMenu li{ </tbody>
margin: 1px 0; </table>
cursor: pointer; <div id="actions" class="hide">
list-style: none outside none; <div class="input-group">
} <select class="form-control m-b" style="width: auto" id="slct_bulk_update">
.dropdown a:hover { <option value="delete">{% trans 'Delete selected' %}</option>
background-color: #f1f1f1 <option value="update">{% trans 'Update selected' %}</option>
} <option value="remove">{% trans 'Remove from this node' %}</option>
</style> <option value="deactive">{% trans 'Deactive selected' %}</option>
<option value="active">{% trans 'Active selected' %}</option>
{% endblock %} </select>
<div class="input-group-btn pull-left" style="padding-left: 5px;">
{% block content %} <button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
<div class="wrapper wrapper-content"> {% trans 'Submit' %}
<div class="row"> </button>
<div class="col-lg-3" id="split-left" style="padding-left: 3px;padding-right: 0">
{% include 'assets/_node_tree.html' %}
</div>
<div class="col-lg-9 animated fadeInRight" id="split-right">
<div class="tree-toggle" style="z-index: 9999">
<div class="btn btn-sm btn-primary tree-toggle-btn">
<i class="fa fa-angle-left fa-x" id="toggle-icon"></i>
</div>
</div>
<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="" 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>
<div class="btn-group" style="float: right">
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>
<ul class="dropdown-menu labels">
{% for label in labels %}
<li><a style="font-weight: bolder">{{ label.name }}#{{ label.value }}</a></li>
{% endfor %}
</ul>
</div>
<table class="table table-striped table-bordered table-hover " id="asset_list_table" style="width: 100%">
<thead>
<tr>
<th class="text-center"><input type="checkbox" class="ipt_check_all"></th>
<th class="text-center">{% trans 'Hostname' %}</th>
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'Hardware' %}</th>
<th class="text-center">{% trans 'Reachable' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div id="actions" class="hide">
<div class="input-group">
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
<option value="delete">{% trans 'Delete selected' %}</option>
<option value="update">{% trans 'Update selected' %}</option>
<option value="remove">{% trans 'Remove from this node' %}</option>
<option value="deactive">{% trans 'Deactive selected' %}</option>
<option value="active">{% trans 'Active selected' %}</option>
</select>
<div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
{% trans 'Submit' %}
</button>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
{% include 'assets/_asset_update_modal.html' %}
{% include 'assets/_asset_import_modal.html' %}
{% include 'assets/_asset_list_modal.html' %} {% include 'assets/_asset_list_modal.html' %}
{% include 'assets/_node_detail_modal.html' %} {% include 'assets/_node_detail_modal.html' %}
{% endblock %} {% endblock %}
...@@ -205,22 +126,6 @@ function initTree() { ...@@ -205,22 +126,6 @@ function initTree() {
}) })
} }
function toggle() {
if (show === 0) {
$("#split-left").hide(500, function () {
$("#split-right").attr("class", "col-lg-12");
$("#toggle-icon").attr("class", "fa fa-angle-right fa-x");
show = 1;
});
} else {
$("#split-right").attr("class", "col-lg-9");
$("#toggle-icon").attr("class", "fa fa-angle-left fa-x");
$("#split-left").show(500);
show = 0;
}
}
function onNodeSelected(event, treeNode) { function onNodeSelected(event, treeNode) {
current_node = treeNode; current_node = treeNode;
current_node_id = treeNode.meta.node.id; current_node_id = treeNode.meta.node.id;
...@@ -261,7 +166,8 @@ function onAssetModalConfirmAddAssetToNode(table) { ...@@ -261,7 +166,8 @@ function onAssetModalConfirmAddAssetToNode(table) {
} }
$(document).ready(function(){ $(document).ready(function(){
initTable(); asset_table = initTable();
initCsvImportExport(asset_table, "{% trans "Asset" %}");
initTree(); initTree();
if(getCookie('show_current_asset') === '1'){ if(getCookie('show_current_asset') === '1'){
...@@ -282,81 +188,6 @@ $(document).ready(function(){ ...@@ -282,81 +188,6 @@ $(document).ready(function(){
$("#asset_list_table_filter input").val(val); $("#asset_list_table_filter input").val(val);
asset_table.search(val).draw(); asset_table.search(val).draw();
}) })
.on('click', '.btn_export', function () {
var assets = asset_table.selected;
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',
params: {
search: search,
node_id: current_node_id || '',
show_current_asset: getCookie('show_current_asset')
}
};
APIExportData(props);
})
.on('click', '#btn_import_confirm', function () {
var file = document.getElementById('id_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);
}
var data_table = $('#asset_list_table').DataTable();
APIImportData({
url: url,
method: "POST",
body: file,
data_table: data_table
});
})
.on('click', '#download_update_template', function () {
var assets = asset_table.selected;
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);
}
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' %}";
if (current_node_id) { if (current_node_id) {
......
{% extends '_base_create_update.html' %} {% load static %} {% load bootstrap3 %}
{% load i18n %}
{% block form %}
<form id="platformForm" method="post" class="form-horizontal">
{% csrf_token %}
{% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.base layout="horizontal" %}
<div class="meta-config">
<div hidden class="windows-config">
{% bootstrap_field meta_form.security layout="horizontal" %}
{% bootstrap_field meta_form.console layout="horizontal" %}
</div>
</div>
{% bootstrap_field form.comment layout="horizontal" %}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
{% endblock %}
{% block custom_foot_js %}
<script type="text/javascript">
function toggleWindowConfig() {
var baseRef = $("#id_base");
var windowConfigRef = $(".windows-config");
var windowConfigInputRef = windowConfigRef.find(".form-control");
if (baseRef.val().toLowerCase() === 'windows') {
windowConfigInputRef.attr('disabled', false);
windowConfigRef.show();
} else {
windowConfigInputRef.attr('disabled', true);
windowConfigRef.hide();
}
}
$(document).ready(function () {
toggleWindowConfig()
})
.on("change", "#id_base", function () {
toggleWindowConfig()
})
.on("submit", "form", function (evt) {
evt.preventDefault();
var form = $("form");
var data = form.serializeObject();
var method = "POST";
var theUrl = '{% url "api-assets:platform-list" %}';
var redirectTo = '{% url "assets:platform-list" %}';
{% if type == "update" %}
theUrl = '{% url 'api-assets:platform-detail' pk=object.id %}';
method = "PUT";
{% endif %}
var metaData = $(".meta-config .form-control").serializeObject();
objectAttrsIsBool(metaData, ['console']);
var metaKeys = Object.keys(metaData);
metaKeys.forEach(function (k, v) {
delete data[k]
});
data.meta = metaData;
console.log(data);
var props = {
url: theUrl,
data: data,
method: method,
form: form,
redirect_to: redirectTo
};
formSubmit(props);
})
</script>
{% endblock %}
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active">
<a href="{% url 'assets:platform-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:platform-update' pk=object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-8" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ object.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td>{% trans 'Name' %}:</td>
<td><b>{{ object.name }}</b></td>
</tr>
<tr>
<td>{% trans 'Base platform' %}:</td>
<td><b>{{ object.base }}</b></td>
</tr>
<tr>
<td>{% trans 'Charset' %}:</td>
<td><b>{{ object.charset }}</b></td>
</tr>
<tr>
<td>{% trans 'Meta' %}:</td>
<td><b>{{ object.meta }}</b></td>
</tr>
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ object.comment }}</b></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
{% endblock %}
{% extends '_base_list.html' %}
{% load i18n static %}
{% block table_search %}
{% endblock %}
{% block table_container %}
<div class="uc pull-left m-r-5">
<a href="{% url "assets:platform-create" %}" class="btn btn-sm btn-primary"> {% trans "Create platform" %} </a>
</div>
<table class="table table-striped table-bordered table-hover " id="platform_list_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Base platform' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script>
var platformTable = 0;
function initTable() {
var options = {
ele: $('#platform_list_table'),
columnDefs: [
{targets: 1, render: function (cellData, tp, rowData, meta) {
cellData = htmlEscape(cellData);
var detailBtn = '<a href="{% url "assets:platform-detail" pk=999 %}">' + cellData + '</a>';
return detailBtn.replace('999', rowData.id);
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
var updateBtn = '<a href="{% url "assets:platform-update" pk=9999 %}" class="btn btn-xs m-l-xs btn-info" disabled>{% trans "Update" %}</a>'.replace('9999', cellData);
var delBtn = '<a class="btn btn-xs btn-danger m-l-xs btn-object-delete" data-uid="{{ DEFAULT_PK }}" disabled>{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
if (!rowData.internal) {
updateBtn = updateBtn.replace('disabled', '');
delBtn = delBtn.replace('disabled', '');
}
$(td).html(updateBtn + delBtn)
}}
],
ajax_url: '{% url "api-assets:platform-list" %}',
columns: [
{data: "id"}, {data: "name"}, {data: "base" },
{data: "comment"}, {data: "id", orderable: false, width: "100px"}
]
};
platformTable = jumpserver.initServerSideDataTable(options);
return platformTable
}
$(document).ready(function(){
initTable();
})
.on('click', '.btn-object-delete', function () {
var $this = $(this);
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
var uid = $this.data('uid');
var theUrl = '{% url "api-assets:platform-detail" pk=0 %}'.replace('0', uid);
objectDelete($this, name, theUrl);
setTimeout( function () {
platformTable.ajax.reload();
}, 3000);
})
</script>
{% endblock %}
...@@ -8,28 +8,7 @@ ...@@ -8,28 +8,7 @@
{% endblock %} {% endblock %}
{% block table_search %} {% block table_search %}
<div class="" style="float: right"> {% include '_csv_import_export.html' %}
<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 %}
...@@ -57,8 +36,6 @@ ...@@ -57,8 +36,6 @@
<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>
...@@ -89,14 +66,13 @@ function initTable() { ...@@ -89,14 +66,13 @@ function initTable() {
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
system_user_table = jumpserver.initServerSideDataTable(options); return jumpserver.initServerSideDataTable(options);
return system_user_table
} }
$(document).ready(function(){ $(document).ready(function(){
initTable(); system_user_table = initTable();
initCsvImportExport(system_user_table, "{% trans 'System user' %}")
}) })
.on('click', '.btn_admin_user_delete', function () { .on('click', '.btn_admin_user_delete', function () {
var $this = $(this); var $this = $(this);
var $data_table = $('#cluster_list_table').DataTable(); var $data_table = $('#cluster_list_table').DataTable();
...@@ -108,125 +84,6 @@ $(document).ready(function(){ ...@@ -108,125 +84,6 @@ $(document).ready(function(){
$data_table.ajax.reload(); $data_table.ajax.reload();
}, 3000); }, 3000);
}) })
.on('click', '#btn_bulk_update', function () {
var action = $('#slct_bulk_update').val();
var $data_table = $('#system_user_list_table').DataTable();
var id_list = [];
var plain_id_list = [];
$data_table.rows({selected: true}).every(function(){
id_list.push({id: this.data().id});
plain_id_list.push(this.data().id);
});
if (id_list === []) {
return false;
}
var the_url = "{% url 'api-assets:system-user-list' %}";
function doDelete() {
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected System Users !!!' %}",
type: "warning",
showCancelButton: true,
cancelButtonText: "{% trans 'Cancel' %}",
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
var success = function() {
var msg = "{% trans 'System Users Deleted.' %}";
swal("{% trans 'System Users Delete' %}", msg, "success");
$('#system_user_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'System Users Deleting failed.' %}";
swal("{% trans 'System Users Delete' %}", msg, "error");
};
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
requestApi({url: url_delete, method: 'DELETE', success: success, error: fail});
$data_table.ajax.reload();
jumpserver.checked = false;
});
}
function doUpdate() {
{# TODO: bulk update the System Users #}
}
switch (action) {
case 'delete':
doDelete();
break;
case 'update':
doUpdate();
break;
default:
break;
}
})
.on('click', '.btn_export', function () {
var system_users = system_user_table.selected;
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 system_users = system_user_table.selected;
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 %}
......
...@@ -12,6 +12,7 @@ app_name = 'assets' ...@@ -12,6 +12,7 @@ app_name = 'assets'
router = BulkRouter() router = BulkRouter()
router.register(r'assets', api.AssetViewSet, 'asset') router.register(r'assets', api.AssetViewSet, 'asset')
router.register(r'platforms', api.AssetPlatformViewSet, 'platform')
router.register(r'admin-users', api.AdminUserViewSet, 'admin-user') router.register(r'admin-users', api.AdminUserViewSet, 'admin-user')
router.register(r'system-users', api.SystemUserViewSet, 'system-user') router.register(r'system-users', api.SystemUserViewSet, 'system-user')
router.register(r'labels', api.LabelViewSet, 'label') router.register(r'labels', api.LabelViewSet, 'label')
...@@ -37,6 +38,8 @@ urlpatterns = [ ...@@ -37,6 +38,8 @@ urlpatterns = [
api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'), api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'),
path('assets/<uuid:pk>/gateway/', path('assets/<uuid:pk>/gateway/',
api.AssetGatewayApi.as_view(), name='asset-gateway'), api.AssetGatewayApi.as_view(), name='asset-gateway'),
path('assets/<uuid:pk>/platform/',
api.AssetPlatformRetrieveApi.as_view(), name='asset-platform-detail'),
path('asset-users/auth-info/', path('asset-users/auth-info/',
api.AssetUserAuthInfoApi.as_view(), name='asset-user-auth-info'), api.AssetUserAuthInfoApi.as_view(), name='asset-user-auth-info'),
......
...@@ -16,6 +16,11 @@ urlpatterns = [ ...@@ -16,6 +16,11 @@ urlpatterns = [
# Asset user view # Asset user view
path('asset/<uuid:pk>/asset-user/', views.AssetUserListView.as_view(), name='asset-user-list'), path('asset/<uuid:pk>/asset-user/', views.AssetUserListView.as_view(), name='asset-user-list'),
path('platform/', views.PlatformListView.as_view(), name='platform-list'),
path('platform/create/', views.PlatformCreateView.as_view(), name='platform-create'),
path('platform/<int:pk>/', views.PlatformDetailView.as_view(), name='platform-detail'),
path('platform/<int:pk>/update/', views.PlatformUpdateView.as_view(), name='platform-update'),
# User asset view # User asset view
path('user-asset/', views.UserAssetListView.as_view(), name='user-asset-list'), path('user-asset/', views.UserAssetListView.as_view(), name='user-asset-list'),
......
# coding:utf-8 # coding:utf-8
from .asset import * from .asset import *
from .platform import *
from .system_user import * from .system_user import *
from .admin_user import * from .admin_user import *
from .label import * from .label import *
......
...@@ -74,7 +74,7 @@ class UserAssetListView(PermissionsMixin, TemplateView): ...@@ -74,7 +74,7 @@ class UserAssetListView(PermissionsMixin, TemplateView):
class AssetCreateView(PermissionsMixin, FormMixin, TemplateView): class AssetCreateView(PermissionsMixin, FormMixin, TemplateView):
model = Asset model = Asset
form_class = forms.AssetCreateForm form_class = forms.AssetCreateUpdateForm
template_name = 'assets/asset_create.html' template_name = 'assets/asset_create.html'
success_url = reverse_lazy('assets:asset-list') success_url = reverse_lazy('assets:asset-list')
permission_classes = [IsOrgAdmin] permission_classes = [IsOrgAdmin]
...@@ -110,7 +110,7 @@ class AssetCreateView(PermissionsMixin, FormMixin, TemplateView): ...@@ -110,7 +110,7 @@ class AssetCreateView(PermissionsMixin, FormMixin, TemplateView):
class AssetUpdateView(PermissionsMixin, UpdateView): class AssetUpdateView(PermissionsMixin, UpdateView):
model = Asset model = Asset
form_class = forms.AssetUpdateForm form_class = forms.AssetCreateUpdateForm
template_name = 'assets/asset_update.html' template_name = 'assets/asset_update.html'
success_url = reverse_lazy('assets:asset-list') success_url = reverse_lazy('assets:asset-list')
permission_classes = [IsOrgAdmin] permission_classes = [IsOrgAdmin]
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.views.generic import TemplateView, CreateView, \ from django.views.generic import (
UpdateView, DeleteView, DetailView TemplateView, CreateView, UpdateView, DeleteView, DetailView
)
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.urls import reverse_lazy, reverse from django.urls import reverse_lazy, reverse
......
# -*- coding: utf-8 -*-
from django.views import generic
from django.utils.translation import ugettext as _
from common.permissions import PermissionsMixin, IsSuperUser
from ..models import Platform
from ..forms import PlatformForm, PlatformMetaForm
__all__ = [
'PlatformListView', 'PlatformUpdateView', 'PlatformCreateView',
'PlatformDetailView',
]
class PlatformListView(PermissionsMixin, generic.TemplateView):
template_name = 'assets/platform_list.html'
permission_classes = (IsSuperUser,)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'app': _('Assets'),
'action': _("Platform list"),
})
return context
class PlatformCreateView(PermissionsMixin, generic.CreateView):
form_class = PlatformForm
permission_classes = (IsSuperUser,)
template_name = 'assets/platform_create_update.html'
model = Platform
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
meta_form = PlatformMetaForm()
context.update({
'app': _('Assets'),
'action': _("Create platform"),
'meta_form': meta_form,
})
return context
class PlatformUpdateView(generic.UpdateView):
form_class = PlatformForm
permission_classes = (IsSuperUser,)
model = Platform
template_name = 'assets/platform_create_update.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
meta_form = PlatformMetaForm(initial=self.object.meta)
context.update({
'app': _('Assets'),
'action': _("Update platform"),
'type': 'update',
'meta_form': meta_form,
})
return context
class PlatformDetailView(generic.DetailView):
permission_classes = (IsSuperUser,)
model = Platform
template_name = 'assets/platform_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'app': _('Assets'),
'action': _("Platform detail"),
})
return context
...@@ -11,6 +11,7 @@ from rest_framework.request import Request ...@@ -11,6 +11,7 @@ from rest_framework.request import Request
from jumpserver.utils import current_request from jumpserver.utils import current_request
from common.utils import get_request_ip, get_logger, get_syslogger from common.utils import get_request_ip, get_logger, get_syslogger
from users.models import User from users.models import User
from users.signals import post_user_change_password
from authentication.signals import post_auth_failed, post_auth_success from authentication.signals import post_auth_failed, post_auth_success
from terminal.models import Session, Command from terminal.models import Session, Command
from common.utils.encode import model_to_json from common.utils.encode import model_to_json
...@@ -18,7 +19,7 @@ from . import models ...@@ -18,7 +19,7 @@ from . import models
from .tasks import write_login_log_async from .tasks import write_login_log_async
logger = get_logger(__name__) logger = get_logger(__name__)
sys_logger = get_syslogger("audits") sys_logger = get_syslogger(__name__)
json_render = JSONRenderer() json_render = JSONRenderer()
...@@ -50,7 +51,7 @@ def create_operate_log(action, sender, resource): ...@@ -50,7 +51,7 @@ def create_operate_log(action, sender, resource):
logger.error("Create operate log error: {}".format(e)) logger.error("Create operate log error: {}".format(e))
@receiver(post_save, dispatch_uid="my_unique_identifier") @receiver(post_save)
def on_object_created_or_update(sender, instance=None, created=False, update_fields=None, **kwargs): def on_object_created_or_update(sender, instance=None, created=False, update_fields=None, **kwargs):
if instance._meta.object_name == 'User' and \ if instance._meta.object_name == 'User' and \
update_fields and 'last_login' in update_fields: update_fields and 'last_login' in update_fields:
...@@ -62,21 +63,27 @@ def on_object_created_or_update(sender, instance=None, created=False, update_fie ...@@ -62,21 +63,27 @@ def on_object_created_or_update(sender, instance=None, created=False, update_fie
create_operate_log(action, sender, instance) create_operate_log(action, sender, instance)
@receiver(post_delete, dispatch_uid="my_unique_identifier") @receiver(post_delete)
def on_object_delete(sender, instance=None, **kwargs): def on_object_delete(sender, instance=None, **kwargs):
create_operate_log(models.OperateLog.ACTION_DELETE, sender, instance) create_operate_log(models.OperateLog.ACTION_DELETE, sender, instance)
@receiver(post_save, sender=User, dispatch_uid="my_unique_identifier") @receiver(post_user_change_password, sender=User)
def on_user_change_password(sender, instance=None, **kwargs): def on_user_change_password(sender, user=None, **kwargs):
if hasattr(instance, '_set_password'): if not current_request:
if not current_request or not current_request.user.is_authenticated: remote_addr = '127.0.0.1'
return change_by = 'System'
with transaction.atomic(): else:
models.PasswordChangeLog.objects.create( remote_addr = get_request_ip(current_request)
user=instance, change_by=current_request.user, if not current_request.user.is_authenticated:
remote_addr=get_request_ip(current_request), change_by = str(user)
) else:
change_by = str(current_request.user)
with transaction.atomic():
models.PasswordChangeLog.objects.create(
user=str(user), change_by=change_by,
remote_addr=remote_addr,
)
def on_audits_log_create(sender, instance=None, **kwargs): def on_audits_log_create(sender, instance=None, **kwargs):
...@@ -95,7 +102,7 @@ def on_audits_log_create(sender, instance=None, **kwargs): ...@@ -95,7 +102,7 @@ def on_audits_log_create(sender, instance=None, **kwargs):
else: else:
return return
data = model_to_json(instance) data = model_to_json(instance, indent=None)
msg = "{} - {}".format(category, data) msg = "{} - {}".format(category, data)
sys_logger.info(msg) sys_logger.info(msg)
......
...@@ -102,47 +102,47 @@ ...@@ -102,47 +102,47 @@
{% block custom_foot_js %} {% block custom_foot_js %}
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script> <script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
<script> <script>
$(document).ready(function() { $(document).ready(function() {
jumpserver.initStaticTable('#login_log_table'); jumpserver.initStaticTable('#login_log_table');
$('#date .input-daterange').datepicker({ $('#date .input-daterange').datepicker({
format: "yyyy-mm-dd", format: "yyyy-mm-dd",
todayBtn: "linked", todayBtn: "linked",
keyboardNavigation: false, keyboardNavigation: false,
forceParse: false, forceParse: false,
calendarWeeks: true, calendarWeeks: true,
autoclose: true autoclose: true
}); });
$('.select2').select2({ $('.select2').select2({
dropdownAutoWidth: true, dropdownAutoWidth: true,
width: 'auto' width: 'auto'
}); });
})
.on('click', '.btn_export', function () {
var date_from = $('#id_date_from').val();
var date_to = $('#id_date_to').val();
var user = $('.select2 option:selected').val();
var keyword = $('#search').val();
$.ajax({
url: "{% url "audits:login-log-export" %}",
method: 'POST',
data: JSON.stringify({
'date_from':date_from,
'date_to':date_to,
'user':user,
'keyword':keyword
}),
dataType: "json",
success: function (data, textStatus) {
window.open(data.redirect)
},
error: function () {
toastr.error('Export failed');
}
}) })
.on('click', '.btn_export', function () { })
var date_from = $('#id_date_from').val(); </script>
var date_to = $('#id_date_to').val();
var user = $('.select2 option:selected').val();
var keyword = $('#search').val();
$.ajax({
url: "{% url "audits:login-log-export" %}",
method: 'POST',
data: JSON.stringify({
'date_from':date_from,
'date_to':date_to,
'user':user,
'keyword':keyword
}),
dataType: "json",
success: function (data, textStatus) {
window.open(data.redirect)
},
error: function () {
toastr.error('Export failed');
}
})
})
</script>
{% endblock %} {% endblock %}
...@@ -2,11 +2,8 @@ ...@@ -2,11 +2,8 @@
# #
from django import forms from django import forms
from django.contrib.auth.forms import AuthenticationForm
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from captcha.fields import CaptchaField from captcha.fields import CaptchaField
from django.conf import settings
from users.utils import get_login_failed_count
class UserLoginForm(forms.Form): class UserLoginForm(forms.Form):
......
...@@ -27,10 +27,17 @@ class IDSpmFilterMixin: ...@@ -27,10 +27,17 @@ class IDSpmFilterMixin:
class SerializerMixin: class SerializerMixin:
def get_serializer_class(self): def get_serializer_class(self):
if self.request.method.lower() == 'get' and\ serializer_class = None
self.request.query_params.get('draw') \ if hasattr(self, 'serializer_classes') and \
and hasattr(self, 'serializer_display_class'): isinstance(self.serializer_classes, dict):
return self.serializer_display_class if self.action == 'list' and self.request.query_params.get('draw'):
serializer_class = self.serializer_classes.get('display')
if serializer_class is None:
serializer_class = self.serializer_classes.get(
self.action, self.serializer_classes.get('default')
)
if serializer_class:
return serializer_class
return super().get_serializer_class() return super().get_serializer_class()
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# #
import re import re
import os import os
import logging
from collections import defaultdict from collections import defaultdict
from django.conf import settings from django.conf import settings
from django.dispatch import receiver from django.dispatch import receiver
...@@ -10,13 +11,13 @@ from django.db import connection ...@@ -10,13 +11,13 @@ from django.db import connection
from django.conf import LazySettings from django.conf import LazySettings
from django.db.utils import ProgrammingError, OperationalError from django.db.utils import ProgrammingError, OperationalError
from jumpserver.utils import get_current_request
from common.utils import get_logger
from .local import thread_local from .local import thread_local
from .signals import django_ready from .signals import django_ready
pattern = re.compile(r'FROM `(\w+)`') pattern = re.compile(r'FROM `(\w+)`')
logger = get_logger(__name__) logger = logging.getLogger("jumpserver.common")
DEBUG_DB = os.environ.get('DEBUG_DB', '0') == '1' DEBUG_DB = os.environ.get('DEBUG_DB', '0') == '1'
...@@ -50,19 +51,29 @@ def on_request_finished_logging_db_query(sender, **kwargs): ...@@ -50,19 +51,29 @@ def on_request_finished_logging_db_query(sender, **kwargs):
counters['total'].time += float(time) counters['total'].time += float(time)
counters = sorted(counters.items(), key=lambda x: x[1]) counters = sorted(counters.items(), key=lambda x: x[1])
if not counters:
return
method = 'GET'
path = '/Unknown'
current_request = get_current_request()
if current_request:
method = current_request.method
path = current_request.get_full_path()
logger.debug(">>> [{}] {}".format(method, path))
for name, counter in counters: for name, counter in counters:
logger.debug("Query {:3} times using {:.2f}s {}".format( logger.debug("Query {:3} times using {:.2f}s {}".format(
counter.counter, counter.time, name) counter.counter, counter.time, name)
) )
@receiver(request_finished)
def on_request_finished_release_local(sender, **kwargs): def on_request_finished_release_local(sender, **kwargs):
thread_local.__release_local__() thread_local.__release_local__()
if settings.DEBUG and DEBUG_DB: if settings.DEBUG and DEBUG_DB:
request_finished.connect(on_request_finished_logging_db_query) request_finished.connect(on_request_finished_logging_db_query)
else:
request_finished.connect(on_request_finished_release_local)
@receiver(django_ready) @receiver(django_ready)
......
...@@ -27,12 +27,12 @@ def combine_seq(s1, s2, callback=None): ...@@ -27,12 +27,12 @@ def combine_seq(s1, s2, callback=None):
return seq return seq
def get_logger(name=None): def get_logger(name=''):
return logging.getLogger('jumpserver.%s' % name) return logging.getLogger('jumpserver.%s' % name)
def get_syslogger(name=None): def get_syslogger(name=''):
return logging.getLogger('jms.%s' % name) return logging.getLogger('syslog.%s' % name)
def timesince(dt, since='', default="just now"): def timesince(dt, since='', default="just now"):
......
...@@ -184,6 +184,7 @@ class Config(dict): ...@@ -184,6 +184,7 @@ class Config(dict):
'ASSETS_PERM_CACHE_ENABLE': False, 'ASSETS_PERM_CACHE_ENABLE': False,
'SYSLOG_ADDR': '', # '192.168.0.1:514' 'SYSLOG_ADDR': '', # '192.168.0.1:514'
'SYSLOG_FACILITY': 'user', 'SYSLOG_FACILITY': 'user',
'SYSLOG_SOCKTYPE': 2,
'PERM_SINGLE_ASSET_TO_UNGROUP_NODE': False, 'PERM_SINGLE_ASSET_TO_UNGROUP_NODE': False,
'WINDOWS_SSH_DEFAULT_SHELL': 'cmd', 'WINDOWS_SSH_DEFAULT_SHELL': 'cmd',
'FLOWER_URL': "127.0.0.1:5555", 'FLOWER_URL': "127.0.0.1:5555",
...@@ -282,10 +283,10 @@ class DynamicConfig: ...@@ -282,10 +283,10 @@ class DynamicConfig:
] ]
if self.get('AUTH_LDAP'): if self.get('AUTH_LDAP'):
backends.insert(0, 'authentication.backends.ldap.LDAPAuthorizationBackend') backends.insert(0, 'authentication.backends.ldap.LDAPAuthorizationBackend')
if self.get('AUTH_OPENID'): if self.static_config.get('AUTH_OPENID'):
backends.insert(0, 'authentication.backends.openid.backends.OpenIDAuthorizationPasswordBackend') backends.insert(0, 'authentication.backends.openid.backends.OpenIDAuthorizationPasswordBackend')
backends.insert(0, 'authentication.backends.openid.backends.OpenIDAuthorizationCodeBackend') backends.insert(0, 'authentication.backends.openid.backends.OpenIDAuthorizationCodeBackend')
if self.get('AUTH_RADIUS'): if self.static_config.get('AUTH_RADIUS'):
backends.insert(0, 'authentication.backends.radius.RadiusBackend') backends.insert(0, 'authentication.backends.radius.RadiusBackend')
return backends return backends
......
...@@ -81,7 +81,7 @@ LOGGING = { ...@@ -81,7 +81,7 @@ LOGGING = {
'propagate': False, 'propagate': False,
}, },
'jumpserver': { 'jumpserver': {
'handlers': ['console', 'file', 'syslog'], 'handlers': ['console', 'file'],
'level': LOG_LEVEL, 'level': LOG_LEVEL,
}, },
'ops.ansible_api': { 'ops.ansible_api': {
...@@ -92,7 +92,7 @@ LOGGING = { ...@@ -92,7 +92,7 @@ LOGGING = {
'handlers': ['console', 'file'], 'handlers': ['console', 'file'],
'level': "INFO", 'level': "INFO",
}, },
'jms.audits': { 'syslog': {
'handlers': ['syslog'], 'handlers': ['syslog'],
'level': 'INFO' 'level': 'INFO'
}, },
...@@ -110,6 +110,7 @@ if CONFIG.SYSLOG_ADDR != '' and len(CONFIG.SYSLOG_ADDR.split(':')) == 2: ...@@ -110,6 +110,7 @@ if CONFIG.SYSLOG_ADDR != '' and len(CONFIG.SYSLOG_ADDR.split(':')) == 2:
'class': 'logging.handlers.SysLogHandler', 'class': 'logging.handlers.SysLogHandler',
'facility': CONFIG.SYSLOG_FACILITY, 'facility': CONFIG.SYSLOG_FACILITY,
'address': (host, int(port)), 'address': (host, int(port)),
'socktype': CONFIG.SYSLOG_SOCKTYPE,
}) })
if not os.path.isdir(LOG_DIR): if not os.path.isdir(LOG_DIR):
......
...@@ -8,7 +8,7 @@ msgid "" ...@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n" "Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-12-06 19:03+0800\n" "POT-Creation-Date: 2019-12-09 15:41+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n" "Language-Team: Jumpserver team<ibuler@qq.com>\n"
...@@ -76,7 +76,7 @@ msgstr "运行参数" ...@@ -76,7 +76,7 @@ msgstr "运行参数"
#: applications/templates/applications/remote_app_list.html:20 #: applications/templates/applications/remote_app_list.html:20
#: applications/templates/applications/user_remote_app_list.html:18 #: applications/templates/applications/user_remote_app_list.html:18
#: assets/forms/domain.py:15 assets/forms/label.py:13 #: assets/forms/domain.py:15 assets/forms/label.py:13
#: assets/models/asset.py:295 assets/models/authbook.py:24 #: assets/models/asset.py:330 assets/models/authbook.py:24
#: assets/models/gathered_user.py:14 assets/serializers/admin_user.py:32 #: assets/models/gathered_user.py:14 assets/serializers/admin_user.py:32
#: assets/serializers/asset_user.py:82 assets/serializers/system_user.py:45 #: assets/serializers/asset_user.py:82 assets/serializers/system_user.py:45
#: assets/templates/assets/admin_user_list.html:44 #: assets/templates/assets/admin_user_list.html:44
...@@ -100,12 +100,12 @@ msgstr "运行参数" ...@@ -100,12 +100,12 @@ msgstr "运行参数"
#: users/templates/users/user_asset_permission.html:90 #: users/templates/users/user_asset_permission.html:90
#: xpack/plugins/change_auth_plan/forms.py:73 #: xpack/plugins/change_auth_plan/forms.py:73
#: xpack/plugins/change_auth_plan/models.py:419 #: xpack/plugins/change_auth_plan/models.py:419
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:44 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:42
#: 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_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_execution_subtask_list.html:13
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:14 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:14
#: xpack/plugins/cloud/models.py:307 #: xpack/plugins/cloud/models.py:307
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:63 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:60
#: xpack/plugins/orgs/templates/orgs/org_list.html:17 #: xpack/plugins/orgs/templates/orgs/org_list.html:17
#: xpack/plugins/vault/forms.py:13 xpack/plugins/vault/forms.py:15 #: xpack/plugins/vault/forms.py:13 xpack/plugins/vault/forms.py:15
msgid "Asset" msgid "Asset"
...@@ -116,11 +116,10 @@ msgstr "资产" ...@@ -116,11 +116,10 @@ msgstr "资产"
#: applications/templates/applications/remote_app_list.html:18 #: applications/templates/applications/remote_app_list.html:18
#: applications/templates/applications/user_remote_app_list.html:16 #: applications/templates/applications/user_remote_app_list.html:16
#: assets/forms/asset.py:21 assets/forms/domain.py:77 assets/forms/user.py:75 #: assets/forms/asset.py:21 assets/forms/domain.py:77 assets/forms/user.py:75
#: assets/forms/user.py:95 assets/models/base.py:28 assets/models/cluster.py:18 #: assets/forms/user.py:95 assets/models/asset.py:136 assets/models/base.py:28
#: assets/models/cmd_filter.py:21 assets/models/domain.py:20 #: assets/models/cluster.py:18 assets/models/cmd_filter.py:21
#: assets/models/group.py:20 assets/models/label.py:18 #: assets/models/domain.py:20 assets/models/group.py:20
#: assets/templates/assets/_node_detail_modal.html:27 #: assets/models/label.py:18 assets/templates/assets/admin_user_detail.html:51
#: assets/templates/assets/admin_user_detail.html:51
#: assets/templates/assets/admin_user_list.html:42 #: assets/templates/assets/admin_user_list.html:42
#: assets/templates/assets/cmd_filter_detail.html:56 #: assets/templates/assets/cmd_filter_detail.html:56
#: assets/templates/assets/cmd_filter_list.html:22 #: assets/templates/assets/cmd_filter_list.html:22
...@@ -128,6 +127,7 @@ msgstr "资产" ...@@ -128,6 +127,7 @@ msgstr "资产"
#: assets/templates/assets/domain_gateway_list.html:62 #: assets/templates/assets/domain_gateway_list.html:62
#: assets/templates/assets/domain_list.html:21 #: assets/templates/assets/domain_list.html:21
#: assets/templates/assets/label_list.html:14 #: assets/templates/assets/label_list.html:14
#: assets/templates/assets/platform_list.html:16
#: assets/templates/assets/system_user_detail.html:53 #: assets/templates/assets/system_user_detail.html:53
#: assets/templates/assets/system_user_list.html:45 ops/models/adhoc.py:37 #: assets/templates/assets/system_user_list.html:45 ops/models/adhoc.py:37
#: ops/templates/ops/task_detail.html:58 ops/templates/ops/task_list.html:11 #: ops/templates/ops/task_detail.html:58 ops/templates/ops/task_list.html:11
...@@ -146,7 +146,7 @@ msgstr "资产" ...@@ -146,7 +146,7 @@ msgstr "资产"
#: terminal/models.py:350 terminal/templates/terminal/base_storage_list.html:32 #: terminal/models.py:350 terminal/templates/terminal/base_storage_list.html:32
#: terminal/templates/terminal/terminal_detail.html:43 #: terminal/templates/terminal/terminal_detail.html:43
#: terminal/templates/terminal/terminal_list.html:30 users/forms.py:162 #: terminal/templates/terminal/terminal_list.html:30 users/forms.py:162
#: users/models/group.py:14 users/models/user.py:443 #: users/models/group.py:14 users/models/user.py:433
#: users/templates/users/_select_user_modal.html:13 #: users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_asset_permission.html:54 #: users/templates/users/user_asset_permission.html:54
#: users/templates/users/user_asset_permission.html:174 #: users/templates/users/user_asset_permission.html:174
...@@ -158,16 +158,16 @@ msgstr "资产" ...@@ -158,16 +158,16 @@ msgstr "资产"
#: users/templates/users/user_pubkey_update.html:57 #: users/templates/users/user_pubkey_update.html:57
#: xpack/plugins/change_auth_plan/forms.py:56 #: xpack/plugins/change_auth_plan/forms.py:56
#: xpack/plugins/change_auth_plan/models.py:64 #: xpack/plugins/change_auth_plan/models.py:64
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:61 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:59
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:12 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:12
#: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:144 #: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:144
#: xpack/plugins/cloud/templates/cloud/account_detail.html:50 #: xpack/plugins/cloud/templates/cloud/account_detail.html:47
#: xpack/plugins/cloud/templates/cloud/account_list.html:12 #: xpack/plugins/cloud/templates/cloud/account_list.html:12
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:56 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:53
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:12 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:12
#: xpack/plugins/gathered_user/models.py:28 #: xpack/plugins/gathered_user/models.py:28
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:16 #: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:16
#: xpack/plugins/orgs/templates/orgs/org_detail.html:52 #: xpack/plugins/orgs/templates/orgs/org_detail.html:47
#: xpack/plugins/orgs/templates/orgs/org_list.html:12 #: xpack/plugins/orgs/templates/orgs/org_list.html:12
msgid "Name" msgid "Name"
msgstr "名称" msgstr "名称"
...@@ -190,7 +190,7 @@ msgstr "参数" ...@@ -190,7 +190,7 @@ msgstr "参数"
#: applications/models/remote_app.py:39 #: applications/models/remote_app.py:39
#: applications/templates/applications/remote_app_detail.html:68 #: applications/templates/applications/remote_app_detail.html:68
#: assets/models/asset.py:174 assets/models/base.py:36 #: assets/models/asset.py:209 assets/models/base.py:36
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:26 #: assets/models/cluster.py:28 assets/models/cmd_filter.py:26
#: assets/models/cmd_filter.py:59 assets/models/group.py:21 #: assets/models/cmd_filter.py:59 assets/models/group.py:21
#: assets/templates/assets/admin_user_detail.html:63 #: assets/templates/assets/admin_user_detail.html:63
...@@ -202,10 +202,10 @@ msgstr "参数" ...@@ -202,10 +202,10 @@ msgstr "参数"
#: orgs/models.py:16 perms/models/base.py:54 #: orgs/models.py:16 perms/models/base.py:54
#: perms/templates/perms/asset_permission_detail.html:93 #: perms/templates/perms/asset_permission_detail.html:93
#: perms/templates/perms/remote_app_permission_detail.html:85 #: perms/templates/perms/remote_app_permission_detail.html:85
#: users/models/user.py:484 users/serializers/group.py:32 #: users/models/user.py:474 users/serializers/group.py:32
#: users/templates/users/user_detail.html:112 #: users/templates/users/user_detail.html:112
#: xpack/plugins/change_auth_plan/models.py:109 #: xpack/plugins/change_auth_plan/models.py:109
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:111
#: xpack/plugins/cloud/models.py:80 xpack/plugins/cloud/models.py:179 #: xpack/plugins/cloud/models.py:80 xpack/plugins/cloud/models.py:179
#: xpack/plugins/gathered_user/models.py:46 #: xpack/plugins/gathered_user/models.py:46
msgid "Created by" msgid "Created by"
...@@ -215,7 +215,7 @@ msgstr "创建者" ...@@ -215,7 +215,7 @@ msgstr "创建者"
# msgstr "创建者" # msgstr "创建者"
#: applications/models/remote_app.py:42 #: applications/models/remote_app.py:42
#: applications/templates/applications/remote_app_detail.html:64 #: applications/templates/applications/remote_app_detail.html:64
#: assets/models/asset.py:175 assets/models/base.py:34 #: assets/models/asset.py:210 assets/models/base.py:34
#: assets/models/cluster.py:26 assets/models/domain.py:23 #: assets/models/cluster.py:26 assets/models/domain.py:23
#: assets/models/gathered_user.py:19 assets/models/group.py:22 #: assets/models/gathered_user.py:19 assets/models/group.py:22
#: assets/models/label.py:25 assets/templates/assets/admin_user_detail.html:59 #: assets/models/label.py:25 assets/templates/assets/admin_user_detail.html:59
...@@ -230,11 +230,11 @@ msgstr "创建者" ...@@ -230,11 +230,11 @@ msgstr "创建者"
#: terminal/templates/terminal/terminal_detail.html:59 #: terminal/templates/terminal/terminal_detail.html:59
#: tickets/templates/tickets/ticket_detail.html:52 users/models/group.py:17 #: tickets/templates/tickets/ticket_detail.html:52 users/models/group.py:17
#: users/templates/users/user_group_detail.html:58 #: users/templates/users/user_group_detail.html:58
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:105 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:103
#: xpack/plugins/cloud/models.py:83 xpack/plugins/cloud/models.py:182 #: xpack/plugins/cloud/models.py:83 xpack/plugins/cloud/models.py:182
#: xpack/plugins/cloud/templates/cloud/account_detail.html:66 #: xpack/plugins/cloud/templates/cloud/account_detail.html:63
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:101 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:98
#: xpack/plugins/orgs/templates/orgs/org_detail.html:60 #: xpack/plugins/orgs/templates/orgs/org_detail.html:55
msgid "Date created" msgid "Date created"
msgstr "创建日期" msgstr "创建日期"
...@@ -244,11 +244,12 @@ msgstr "创建日期" ...@@ -244,11 +244,12 @@ msgstr "创建日期"
#: applications/templates/applications/remote_app_detail.html:72 #: applications/templates/applications/remote_app_detail.html:72
#: applications/templates/applications/remote_app_list.html:21 #: applications/templates/applications/remote_app_list.html:21
#: applications/templates/applications/user_remote_app_list.html:19 #: applications/templates/applications/user_remote_app_list.html:19
#: assets/models/asset.py:176 assets/models/base.py:33 #: assets/models/asset.py:141 assets/models/asset.py:211
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:23 #: assets/models/base.py:33 assets/models/cluster.py:29
#: assets/models/cmd_filter.py:56 assets/models/domain.py:21 #: assets/models/cmd_filter.py:23 assets/models/cmd_filter.py:56
#: assets/models/domain.py:53 assets/models/group.py:23 #: assets/models/domain.py:21 assets/models/domain.py:53
#: assets/models/label.py:23 assets/templates/assets/admin_user_detail.html:67 #: assets/models/group.py:23 assets/models/label.py:23
#: assets/templates/assets/admin_user_detail.html:67
#: assets/templates/assets/admin_user_list.html:48 #: assets/templates/assets/admin_user_list.html:48
#: assets/templates/assets/asset_detail.html:128 #: assets/templates/assets/asset_detail.html:128
#: assets/templates/assets/cmd_filter_detail.html:60 #: assets/templates/assets/cmd_filter_detail.html:60
...@@ -257,6 +258,7 @@ msgstr "创建日期" ...@@ -257,6 +258,7 @@ msgstr "创建日期"
#: assets/templates/assets/domain_detail.html:71 #: assets/templates/assets/domain_detail.html:71
#: assets/templates/assets/domain_gateway_list.html:67 #: assets/templates/assets/domain_gateway_list.html:67
#: assets/templates/assets/domain_list.html:24 #: assets/templates/assets/domain_list.html:24
#: assets/templates/assets/platform_list.html:18
#: assets/templates/assets/system_user_detail.html:99 #: assets/templates/assets/system_user_detail.html:99
#: assets/templates/assets/system_user_list.html:53 ops/models/adhoc.py:43 #: assets/templates/assets/system_user_list.html:53 ops/models/adhoc.py:43
#: orgs/models.py:18 perms/models/base.py:56 #: orgs/models.py:18 perms/models/base.py:56
...@@ -266,20 +268,20 @@ msgstr "创建日期" ...@@ -266,20 +268,20 @@ msgstr "创建日期"
#: terminal/models.py:357 terminal/templates/terminal/base_storage_list.html:34 #: terminal/models.py:357 terminal/templates/terminal/base_storage_list.html:34
#: terminal/templates/terminal/terminal_detail.html:63 #: terminal/templates/terminal/terminal_detail.html:63
#: tickets/templates/tickets/ticket_detail.html:104 users/models/group.py:15 #: tickets/templates/tickets/ticket_detail.html:104 users/models/group.py:15
#: users/models/user.py:476 users/templates/users/user_detail.html:130 #: users/models/user.py:466 users/templates/users/user_detail.html:130
#: users/templates/users/user_group_detail.html:62 #: users/templates/users/user_group_detail.html:62
#: users/templates/users/user_group_list.html:37 #: users/templates/users/user_group_list.html:37
#: users/templates/users/user_profile.html:138 #: users/templates/users/user_profile.html:138
#: xpack/plugins/change_auth_plan/models.py:105 #: xpack/plugins/change_auth_plan/models.py:105
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:117 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:115
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:19 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:19
#: xpack/plugins/cloud/models.py:77 xpack/plugins/cloud/models.py:173 #: xpack/plugins/cloud/models.py:77 xpack/plugins/cloud/models.py:173
#: xpack/plugins/cloud/templates/cloud/account_detail.html:70 #: xpack/plugins/cloud/templates/cloud/account_detail.html:67
#: xpack/plugins/cloud/templates/cloud/account_list.html:15 #: xpack/plugins/cloud/templates/cloud/account_list.html:15
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:105 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:102
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:18 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:18
#: xpack/plugins/gathered_user/models.py:42 #: xpack/plugins/gathered_user/models.py:42
#: xpack/plugins/orgs/templates/orgs/org_detail.html:64 #: xpack/plugins/orgs/templates/orgs/org_detail.html:59
#: xpack/plugins/orgs/templates/orgs/org_list.html:23 #: xpack/plugins/orgs/templates/orgs/org_list.html:23
msgid "Comment" msgid "Comment"
msgstr "备注" msgstr "备注"
...@@ -305,14 +307,15 @@ msgstr "远程应用" ...@@ -305,14 +307,15 @@ msgstr "远程应用"
#: assets/templates/assets/domain_create_update.html:16 #: assets/templates/assets/domain_create_update.html:16
#: assets/templates/assets/gateway_create_update.html:54 #: assets/templates/assets/gateway_create_update.html:54
#: assets/templates/assets/label_create_update.html:18 #: assets/templates/assets/label_create_update.html:18
#: assets/templates/assets/platform_create_update.html:16
#: perms/templates/perms/asset_permission_create_update.html:81 #: perms/templates/perms/asset_permission_create_update.html:81
#: perms/templates/perms/remote_app_permission_create_update.html:82 #: perms/templates/perms/remote_app_permission_create_update.html:82
#: settings/templates/settings/basic_setting.html:64 #: settings/templates/settings/basic_setting.html:45
#: settings/templates/settings/email_content_setting.html:54 #: settings/templates/settings/email_content_setting.html:35
#: settings/templates/settings/email_setting.html:65 #: settings/templates/settings/email_setting.html:46
#: settings/templates/settings/ldap_setting.html:64 #: settings/templates/settings/ldap_setting.html:45
#: settings/templates/settings/security_setting.html:73 #: settings/templates/settings/security_setting.html:54
#: settings/templates/settings/terminal_setting.html:76 #: settings/templates/settings/terminal_setting.html:53
#: terminal/templates/terminal/base_storage_create_update.html:12 #: terminal/templates/terminal/base_storage_create_update.html:12
#: terminal/templates/terminal/terminal_update.html:43 #: terminal/templates/terminal/terminal_update.html:43
#: users/templates/users/_user.html:51 #: users/templates/users/_user.html:51
...@@ -324,13 +327,13 @@ msgstr "远程应用" ...@@ -324,13 +327,13 @@ msgstr "远程应用"
#: users/templates/users/user_profile_update.html:67 #: users/templates/users/user_profile_update.html:67
#: users/templates/users/user_pubkey_update.html:74 #: users/templates/users/user_pubkey_update.html:74
#: users/templates/users/user_pubkey_update.html:80 #: users/templates/users/user_pubkey_update.html:80
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:69 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:67
#: xpack/plugins/cloud/templates/cloud/account_create_update.html:33 #: xpack/plugins/cloud/templates/cloud/account_create_update.html:29
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:53 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:49
#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:44 #: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:40
#: xpack/plugins/interface/templates/interface/interface.html:72 #: xpack/plugins/interface/templates/interface/interface.html:72
#: xpack/plugins/orgs/templates/orgs/org_create_update.html:33 #: xpack/plugins/orgs/templates/orgs/org_create_update.html:29
#: xpack/plugins/vault/templates/vault/vault_create.html:45 #: xpack/plugins/vault/templates/vault/vault_create.html:41
msgid "Reset" msgid "Reset"
msgstr "重置" msgstr "重置"
...@@ -345,15 +348,16 @@ msgstr "重置" ...@@ -345,15 +348,16 @@ msgstr "重置"
#: assets/templates/assets/domain_create_update.html:17 #: assets/templates/assets/domain_create_update.html:17
#: assets/templates/assets/gateway_create_update.html:55 #: assets/templates/assets/gateway_create_update.html:55
#: assets/templates/assets/label_create_update.html:19 #: assets/templates/assets/label_create_update.html:19
#: assets/templates/assets/platform_create_update.html:17
#: audits/templates/audits/login_log_list.html:95 #: audits/templates/audits/login_log_list.html:95
#: perms/templates/perms/asset_permission_create_update.html:82 #: perms/templates/perms/asset_permission_create_update.html:82
#: perms/templates/perms/remote_app_permission_create_update.html:83 #: perms/templates/perms/remote_app_permission_create_update.html:83
#: settings/templates/settings/basic_setting.html:65 #: settings/templates/settings/basic_setting.html:46
#: settings/templates/settings/email_content_setting.html:55 #: settings/templates/settings/email_content_setting.html:36
#: settings/templates/settings/email_setting.html:66 #: settings/templates/settings/email_setting.html:47
#: settings/templates/settings/ldap_setting.html:67 #: settings/templates/settings/ldap_setting.html:48
#: settings/templates/settings/security_setting.html:74 #: settings/templates/settings/security_setting.html:55
#: settings/templates/settings/terminal_setting.html:78 #: settings/templates/settings/terminal_setting.html:55
#: terminal/templates/terminal/base_storage_create_update.html:13 #: terminal/templates/terminal/base_storage_create_update.html:13
#: terminal/templates/terminal/command_list.html:47 #: terminal/templates/terminal/command_list.html:47
#: terminal/templates/terminal/session_list.html:50 #: terminal/templates/terminal/session_list.html:50
...@@ -361,13 +365,13 @@ msgstr "重置" ...@@ -361,13 +365,13 @@ msgstr "重置"
#: users/templates/users/_user.html:52 #: users/templates/users/_user.html:52
#: users/templates/users/forgot_password.html:42 #: users/templates/users/forgot_password.html:42
#: users/templates/users/user_bulk_update.html:24 #: users/templates/users/user_bulk_update.html:24
#: users/templates/users/user_list.html:61 #: users/templates/users/user_list.html:57
#: users/templates/users/user_password_update.html:76 #: users/templates/users/user_password_update.html:76
#: users/templates/users/user_profile_update.html:68 #: users/templates/users/user_profile_update.html:68
#: users/templates/users/user_pubkey_update.html:81 #: users/templates/users/user_pubkey_update.html:81
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:70 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:68
#: xpack/plugins/interface/templates/interface/interface.html:74 #: xpack/plugins/interface/templates/interface/interface.html:74
#: xpack/plugins/vault/templates/vault/vault_create.html:46 #: xpack/plugins/vault/templates/vault/vault_create.html:42
msgid "Submit" msgid "Submit"
msgstr "提交" msgstr "提交"
...@@ -389,8 +393,8 @@ msgstr "提交" ...@@ -389,8 +393,8 @@ msgstr "提交"
#: perms/templates/perms/remote_app_permission_detail.html:13 #: perms/templates/perms/remote_app_permission_detail.html:13
#: perms/templates/perms/remote_app_permission_remote_app.html:13 #: perms/templates/perms/remote_app_permission_remote_app.html:13
#: perms/templates/perms/remote_app_permission_user.html:13 #: perms/templates/perms/remote_app_permission_user.html:13
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:17 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:13
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:20 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:18
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:17 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:17
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:106 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:106
#: xpack/plugins/change_auth_plan/views.py:91 #: xpack/plugins/change_auth_plan/views.py:91
...@@ -405,7 +409,7 @@ msgstr "详情" ...@@ -405,7 +409,7 @@ msgstr "详情"
#: assets/templates/assets/admin_user_list.html:72 #: assets/templates/assets/admin_user_list.html:72
#: assets/templates/assets/asset_detail.html:24 #: assets/templates/assets/asset_detail.html:24
#: assets/templates/assets/asset_list.html:78 #: assets/templates/assets/asset_list.html:78
#: assets/templates/assets/asset_list.html:168 #: assets/templates/assets/asset_list.html:167
#: assets/templates/assets/cmd_filter_detail.html:24 #: assets/templates/assets/cmd_filter_detail.html:24
#: assets/templates/assets/cmd_filter_list.html:56 #: assets/templates/assets/cmd_filter_list.html:56
#: assets/templates/assets/cmd_filter_rule_list.html:81 #: assets/templates/assets/cmd_filter_rule_list.html:81
...@@ -414,6 +418,7 @@ msgstr "详情" ...@@ -414,6 +418,7 @@ msgstr "详情"
#: assets/templates/assets/domain_gateway_list.html:92 #: assets/templates/assets/domain_gateway_list.html:92
#: assets/templates/assets/domain_list.html:50 #: assets/templates/assets/domain_list.html:50
#: assets/templates/assets/label_list.html:39 #: assets/templates/assets/label_list.html:39
#: assets/templates/assets/platform_list.html:40
#: assets/templates/assets/system_user_detail.html:21 #: assets/templates/assets/system_user_detail.html:21
#: assets/templates/assets/system_user_list.html:27 #: assets/templates/assets/system_user_list.html:27
#: assets/templates/assets/system_user_list.html:79 audits/models.py:34 #: assets/templates/assets/system_user_list.html:79 audits/models.py:34
...@@ -431,19 +436,19 @@ msgstr "详情" ...@@ -431,19 +436,19 @@ msgstr "详情"
#: users/templates/users/user_group_list.html:20 #: users/templates/users/user_group_list.html:20
#: users/templates/users/user_group_list.html:71 #: users/templates/users/user_group_list.html:71
#: users/templates/users/user_list.html:20 #: users/templates/users/user_list.html:20
#: users/templates/users/user_list.html:107 #: users/templates/users/user_list.html:103
#: users/templates/users/user_list.html:110 #: users/templates/users/user_list.html:106
#: users/templates/users/user_profile.html:181 #: users/templates/users/user_profile.html:181
#: users/templates/users/user_profile.html:191 #: users/templates/users/user_profile.html:191
#: users/templates/users/user_profile.html:201 #: users/templates/users/user_profile.html:201
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:29 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:27
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:56 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:56
#: xpack/plugins/cloud/templates/cloud/account_detail.html:23 #: xpack/plugins/cloud/templates/cloud/account_detail.html:20
#: xpack/plugins/cloud/templates/cloud/account_list.html:40 #: xpack/plugins/cloud/templates/cloud/account_list.html:40
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:29 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:26
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:57 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:57
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:46 #: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:46
#: xpack/plugins/orgs/templates/orgs/org_detail.html:25 #: xpack/plugins/orgs/templates/orgs/org_detail.html:20
#: xpack/plugins/orgs/templates/orgs/org_list.html:93 #: xpack/plugins/orgs/templates/orgs/org_list.html:93
msgid "Update" msgid "Update"
msgstr "更新" msgstr "更新"
...@@ -453,7 +458,7 @@ msgstr "更新" ...@@ -453,7 +458,7 @@ msgstr "更新"
#: assets/templates/assets/admin_user_detail.html:23 #: assets/templates/assets/admin_user_detail.html:23
#: assets/templates/assets/admin_user_list.html:73 #: assets/templates/assets/admin_user_list.html:73
#: assets/templates/assets/asset_detail.html:28 #: assets/templates/assets/asset_detail.html:28
#: assets/templates/assets/asset_list.html:169 #: assets/templates/assets/asset_list.html:168
#: assets/templates/assets/cmd_filter_detail.html:28 #: assets/templates/assets/cmd_filter_detail.html:28
#: assets/templates/assets/cmd_filter_list.html:57 #: assets/templates/assets/cmd_filter_list.html:57
#: assets/templates/assets/cmd_filter_rule_list.html:82 #: assets/templates/assets/cmd_filter_rule_list.html:82
...@@ -462,6 +467,7 @@ msgstr "更新" ...@@ -462,6 +467,7 @@ msgstr "更新"
#: assets/templates/assets/domain_gateway_list.html:93 #: assets/templates/assets/domain_gateway_list.html:93
#: assets/templates/assets/domain_list.html:51 #: assets/templates/assets/domain_list.html:51
#: assets/templates/assets/label_list.html:40 #: assets/templates/assets/label_list.html:40
#: assets/templates/assets/platform_list.html:41
#: assets/templates/assets/system_user_detail.html:25 #: assets/templates/assets/system_user_detail.html:25
#: assets/templates/assets/system_user_list.html:80 audits/models.py:35 #: assets/templates/assets/system_user_list.html:80 audits/models.py:35
#: authentication/templates/authentication/_access_key_modal.html:65 #: authentication/templates/authentication/_access_key_modal.html:65
...@@ -477,16 +483,16 @@ msgstr "更新" ...@@ -477,16 +483,16 @@ msgstr "更新"
#: users/templates/users/user_detail.html:31 #: users/templates/users/user_detail.html:31
#: users/templates/users/user_group_detail.html:27 #: users/templates/users/user_group_detail.html:27
#: users/templates/users/user_group_list.html:73 #: users/templates/users/user_group_list.html:73
#: users/templates/users/user_list.html:117 #: users/templates/users/user_list.html:111
#: users/templates/users/user_list.html:121 #: users/templates/users/user_list.html:115
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:33 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:31
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:58 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:58
#: xpack/plugins/cloud/templates/cloud/account_detail.html:27 #: xpack/plugins/cloud/templates/cloud/account_detail.html:24
#: xpack/plugins/cloud/templates/cloud/account_list.html:42 #: xpack/plugins/cloud/templates/cloud/account_list.html:42
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:33 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:30
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:58 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:58
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:47 #: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:47
#: xpack/plugins/orgs/templates/orgs/org_detail.html:29 #: xpack/plugins/orgs/templates/orgs/org_detail.html:24
#: xpack/plugins/orgs/templates/orgs/org_list.html:95 #: xpack/plugins/orgs/templates/orgs/org_list.html:95
msgid "Delete" msgid "Delete"
msgstr "删除" msgstr "删除"
...@@ -520,6 +526,7 @@ msgstr "创建远程应用" ...@@ -520,6 +526,7 @@ msgstr "创建远程应用"
#: assets/templates/assets/domain_gateway_list.html:68 #: assets/templates/assets/domain_gateway_list.html:68
#: assets/templates/assets/domain_list.html:25 #: assets/templates/assets/domain_list.html:25
#: assets/templates/assets/label_list.html:17 #: assets/templates/assets/label_list.html:17
#: assets/templates/assets/platform_list.html:19
#: assets/templates/assets/system_user_list.html:54 audits/models.py:39 #: assets/templates/assets/system_user_list.html:54 audits/models.py:39
#: audits/templates/audits/operate_log_list.html:45 #: audits/templates/audits/operate_log_list.html:45
#: audits/templates/audits/operate_log_list.html:71 #: audits/templates/audits/operate_log_list.html:71
...@@ -546,7 +553,7 @@ msgstr "创建远程应用" ...@@ -546,7 +553,7 @@ msgstr "创建远程应用"
#: 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_execution_subtask_list.html:18
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:20 #: 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/account_list.html:16
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:72 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:67
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:19 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:19
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:20 #: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:20
#: xpack/plugins/orgs/templates/orgs/org_list.html:24 #: xpack/plugins/orgs/templates/orgs/org_list.html:24
...@@ -604,13 +611,13 @@ msgstr "不能包含特殊字符:[ {} ]" ...@@ -604,13 +611,13 @@ msgstr "不能包含特殊字符:[ {} ]"
msgid "* The contains characters that are not allowed" msgid "* The contains characters that are not allowed"
msgstr "* 包含不被允许的字符" msgstr "* 包含不被允许的字符"
#: assets/forms/asset.py:25 assets/models/asset.py:140 #: assets/forms/asset.py:25 assets/models/asset.py:176
#: assets/models/domain.py:50 #: assets/models/domain.py:50
#: assets/templates/assets/domain_gateway_list.html:64 #: assets/templates/assets/domain_gateway_list.html:64
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
#: assets/forms/asset.py:56 assets/models/asset.py:145 #: assets/forms/asset.py:56 assets/models/asset.py:180
#: assets/models/user.py:110 assets/templates/assets/asset_detail.html:186 #: assets/models/user.py:110 assets/templates/assets/asset_detail.html:186
#: assets/templates/assets/asset_detail.html:194 #: assets/templates/assets/asset_detail.html:194
#: assets/templates/assets/system_user_assets.html:87 #: assets/templates/assets/system_user_assets.html:87
...@@ -622,10 +629,10 @@ msgid "Nodes" ...@@ -622,10 +629,10 @@ msgid "Nodes"
msgstr "节点" msgstr "节点"
#: assets/forms/asset.py:59 assets/forms/asset.py:106 #: assets/forms/asset.py:59 assets/forms/asset.py:106
#: assets/models/asset.py:149 assets/models/cluster.py:19 #: assets/models/asset.py:184 assets/models/cluster.py:19
#: assets/models/user.py:68 assets/templates/assets/asset_detail.html:72 #: assets/models/user.py:68 assets/templates/assets/asset_detail.html:72
#: templates/_nav.html:44 xpack/plugins/cloud/models.py:161 #: templates/_nav.html:44 xpack/plugins/cloud/models.py:161
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:68 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:65
#: xpack/plugins/orgs/templates/orgs/org_list.html:19 #: xpack/plugins/orgs/templates/orgs/org_list.html:19
msgid "Admin user" msgid "Admin user"
msgstr "管理用户" msgstr "管理用户"
...@@ -639,7 +646,7 @@ msgid "Label" ...@@ -639,7 +646,7 @@ msgid "Label"
msgstr "标签" msgstr "标签"
#: assets/forms/asset.py:65 assets/forms/asset.py:112 #: assets/forms/asset.py:65 assets/forms/asset.py:112
#: assets/models/asset.py:144 assets/models/domain.py:26 #: assets/models/asset.py:179 assets/models/domain.py:26
#: assets/models/domain.py:52 assets/templates/assets/asset_detail.html:76 #: assets/models/domain.py:52 assets/templates/assets/asset_detail.html:76
#: assets/templates/assets/user_asset_list.html:80 #: assets/templates/assets/user_asset_list.html:80
#: xpack/plugins/orgs/templates/orgs/org_list.html:18 #: xpack/plugins/orgs/templates/orgs/org_list.html:18
...@@ -661,8 +668,8 @@ msgstr "网域" ...@@ -661,8 +668,8 @@ msgstr "网域"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:55 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:55
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:15 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:15
#: xpack/plugins/cloud/models.py:157 #: xpack/plugins/cloud/models.py:157
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:64 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:61
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:64 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:61
msgid "Node" msgid "Node"
msgstr "节点" msgstr "节点"
...@@ -687,7 +694,7 @@ msgstr "如果有多个的互相隔离的网络,设置资产属于的网域, ...@@ -687,7 +694,7 @@ msgstr "如果有多个的互相隔离的网络,设置资产属于的网域,
#: assets/forms/domain.py:17 assets/forms/label.py:15 #: assets/forms/domain.py:17 assets/forms/label.py:15
#: perms/templates/perms/asset_permission_asset.html:74 #: perms/templates/perms/asset_permission_asset.html:74
#: xpack/plugins/change_auth_plan/forms.py:64 #: xpack/plugins/change_auth_plan/forms.py:64
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:74 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:70
msgid "Select assets" msgid "Select assets"
msgstr "选择资产" msgstr "选择资产"
...@@ -719,7 +726,7 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC" ...@@ -719,7 +726,7 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC"
#: ops/models/adhoc.py:189 perms/templates/perms/asset_permission_list.html:205 #: ops/models/adhoc.py:189 perms/templates/perms/asset_permission_list.html:205
#: perms/templates/perms/remote_app_permission_user.html:50 #: perms/templates/perms/remote_app_permission_user.html:50
#: settings/templates/settings/_ldap_list_users_modal.html:31 users/forms.py:14 #: settings/templates/settings/_ldap_list_users_modal.html:31 users/forms.py:14
#: users/forms.py:161 users/models/user.py:441 #: users/forms.py:161 users/models/user.py:431
#: users/templates/users/_select_user_modal.html:14 #: users/templates/users/_select_user_modal.html:14
#: users/templates/users/user_detail.html:68 #: users/templates/users/user_detail.html:68
#: users/templates/users/user_list.html:36 #: users/templates/users/user_list.html:36
...@@ -727,14 +734,18 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC" ...@@ -727,14 +734,18 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC"
#: xpack/plugins/change_auth_plan/forms.py:58 #: xpack/plugins/change_auth_plan/forms.py:58
#: xpack/plugins/change_auth_plan/models.py:66 #: xpack/plugins/change_auth_plan/models.py:66
#: xpack/plugins/change_auth_plan/models.py:415 #: xpack/plugins/change_auth_plan/models.py:415
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:65 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:63
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:53 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:53
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:12 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:12
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:13 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:13
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:64 #: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:69
msgid "Username" msgid "Username"
msgstr "用户名" msgstr "用户名"
#: assets/forms/platform.py:19 assets/templates/assets/platform_list.html:17
msgid "Base platform"
msgstr "基础平台"
#: assets/forms/user.py:26 #: assets/forms/user.py:26
msgid "Password or private key passphrase" msgid "Password or private key passphrase"
msgstr "密码或密钥密码" msgstr "密码或密钥密码"
...@@ -760,7 +771,7 @@ msgstr "密码" ...@@ -760,7 +771,7 @@ msgstr "密码"
#: assets/forms/user.py:30 assets/serializers/asset_user.py:71 #: assets/forms/user.py:30 assets/serializers/asset_user.py:71
#: assets/templates/assets/_asset_user_auth_update_modal.html:27 #: assets/templates/assets/_asset_user_auth_update_modal.html:27
#: users/models/user.py:470 #: users/models/user.py:460
msgid "Private key" msgid "Private key"
msgstr "ssh私钥" msgstr "ssh私钥"
...@@ -800,7 +811,23 @@ msgstr "如果选择手动登录模式,用户名和密码可以不填写" ...@@ -800,7 +811,23 @@ msgstr "如果选择手动登录模式,用户名和密码可以不填写"
msgid "Use comma split multi command, ex: /bin/whoami,/bin/ifconfig" msgid "Use comma split multi command, ex: /bin/whoami,/bin/ifconfig"
msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig" msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig"
#: assets/models/asset.py:135 assets/models/domain.py:49 #: assets/models/asset.py:137
msgid "Base"
msgstr "基础"
#: assets/models/asset.py:138
msgid "Charset"
msgstr "编码"
#: assets/models/asset.py:139 tickets/models/ticket.py:38
msgid "Meta"
msgstr "元数据"
#: assets/models/asset.py:140
msgid "Internal"
msgstr "内部的"
#: assets/models/asset.py:171 assets/models/domain.py:49
#: assets/serializers/asset_user.py:28 #: assets/serializers/asset_user.py:28
#: assets/templates/assets/_asset_list_modal.html:47 #: assets/templates/assets/_asset_list_modal.html:47
#: assets/templates/assets/_asset_user_list.html:20 #: assets/templates/assets/_asset_user_list.html:20
...@@ -812,12 +839,12 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig" ...@@ -812,12 +839,12 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig"
#: perms/templates/perms/asset_permission_list.html:207 #: perms/templates/perms/asset_permission_list.html:207
#: settings/forms/terminal.py:16 users/templates/users/_granted_assets.html:31 #: settings/forms/terminal.py:16 users/templates/users/_granted_assets.html:31
#: users/templates/users/user_asset_permission.html:176 #: users/templates/users/user_asset_permission.html:176
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:54 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:50
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:63 #: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:68
msgid "IP" msgid "IP"
msgstr "IP" msgstr "IP"
#: assets/models/asset.py:136 assets/serializers/asset_user.py:27 #: assets/models/asset.py:172 assets/serializers/asset_user.py:27
#: assets/serializers/gathered_user.py:20 #: assets/serializers/gathered_user.py:20
#: assets/templates/assets/_asset_list_modal.html:46 #: assets/templates/assets/_asset_list_modal.html:46
#: assets/templates/assets/_asset_user_auth_update_modal.html:9 #: assets/templates/assets/_asset_user_auth_update_modal.html:9
...@@ -829,12 +856,12 @@ msgstr "IP" ...@@ -829,12 +856,12 @@ msgstr "IP"
#: perms/templates/perms/asset_permission_list.html:208 #: perms/templates/perms/asset_permission_list.html:208
#: settings/forms/terminal.py:15 users/templates/users/_granted_assets.html:30 #: settings/forms/terminal.py:15 users/templates/users/_granted_assets.html:30
#: users/templates/users/user_asset_permission.html:177 #: users/templates/users/user_asset_permission.html:177
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:53 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:49
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:62 #: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:67
msgid "Hostname" msgid "Hostname"
msgstr "主机名" msgstr "主机名"
#: assets/models/asset.py:139 assets/models/domain.py:51 #: assets/models/asset.py:175 assets/models/domain.py:51
#: assets/models/user.py:113 assets/templates/assets/asset_detail.html:68 #: assets/models/user.py:113 assets/templates/assets/asset_detail.html:68
#: assets/templates/assets/domain_gateway_list.html:65 #: assets/templates/assets/domain_gateway_list.html:65
#: assets/templates/assets/system_user_detail.html:65 #: assets/templates/assets/system_user_detail.html:65
...@@ -844,91 +871,91 @@ msgstr "主机名" ...@@ -844,91 +871,91 @@ msgstr "主机名"
msgid "Protocol" msgid "Protocol"
msgstr "协议" msgstr "协议"
#: assets/models/asset.py:142 assets/serializers/asset.py:68 #: assets/models/asset.py:177 assets/serializers/asset.py:69
#: assets/templates/assets/asset_create.html:24 #: assets/templates/assets/asset_create.html:24
#: assets/templates/assets/user_asset_list.html:77 #: assets/templates/assets/user_asset_list.html:77
#: perms/serializers/user_permission.py:48 #: perms/serializers/user_permission.py:48
msgid "Protocols" msgid "Protocols"
msgstr "协议组" msgstr "协议组"
#: assets/models/asset.py:143 assets/templates/assets/asset_detail.html:100 #: assets/models/asset.py:178 assets/templates/assets/asset_detail.html:100
#: assets/templates/assets/user_asset_list.html:78 #: assets/templates/assets/user_asset_list.html:78
msgid "Platform" msgid "Platform"
msgstr "系统平台" msgstr "系统平台"
#: assets/models/asset.py:146 assets/models/authbook.py:27 #: assets/models/asset.py:181 assets/models/authbook.py:27
#: assets/models/cmd_filter.py:22 assets/models/domain.py:54 #: assets/models/cmd_filter.py:22 assets/models/domain.py:54
#: assets/models/label.py:22 assets/templates/assets/asset_detail.html:108 #: assets/models/label.py:22 assets/templates/assets/asset_detail.html:108
#: authentication/models.py:45 #: authentication/models.py:45
msgid "Is active" msgid "Is active"
msgstr "激活" msgstr "激活"
#: assets/models/asset.py:152 assets/templates/assets/asset_detail.html:64 #: assets/models/asset.py:187 assets/templates/assets/asset_detail.html:64
msgid "Public IP" msgid "Public IP"
msgstr "公网IP" msgstr "公网IP"
#: assets/models/asset.py:153 assets/templates/assets/asset_detail.html:116 #: assets/models/asset.py:188 assets/templates/assets/asset_detail.html:116
msgid "Asset number" msgid "Asset number"
msgstr "资产编号" msgstr "资产编号"
#: assets/models/asset.py:156 assets/templates/assets/asset_detail.html:80 #: assets/models/asset.py:191 assets/templates/assets/asset_detail.html:80
msgid "Vendor" msgid "Vendor"
msgstr "制造商" msgstr "制造商"
#: assets/models/asset.py:157 assets/templates/assets/asset_detail.html:84 #: assets/models/asset.py:192 assets/templates/assets/asset_detail.html:84
msgid "Model" msgid "Model"
msgstr "型号" msgstr "型号"
#: assets/models/asset.py:158 assets/templates/assets/asset_detail.html:112 #: assets/models/asset.py:193 assets/templates/assets/asset_detail.html:112
msgid "Serial number" msgid "Serial number"
msgstr "序列号" msgstr "序列号"
#: assets/models/asset.py:160 #: assets/models/asset.py:195
msgid "CPU model" msgid "CPU model"
msgstr "CPU型号" msgstr "CPU型号"
#: assets/models/asset.py:161 #: assets/models/asset.py:196
#: xpack/plugins/license/templates/license/license_detail.html:71 #: xpack/plugins/license/templates/license/license_detail.html:80
msgid "CPU count" msgid "CPU count"
msgstr "CPU数量" msgstr "CPU数量"
#: assets/models/asset.py:162 #: assets/models/asset.py:197
msgid "CPU cores" msgid "CPU cores"
msgstr "CPU核数" msgstr "CPU核数"
#: assets/models/asset.py:163 #: assets/models/asset.py:198
msgid "CPU vcpus" msgid "CPU vcpus"
msgstr "CPU总数" msgstr "CPU总数"
#: assets/models/asset.py:164 assets/templates/assets/asset_detail.html:92 #: assets/models/asset.py:199 assets/templates/assets/asset_detail.html:92
msgid "Memory" msgid "Memory"
msgstr "内存" msgstr "内存"
#: assets/models/asset.py:165 #: assets/models/asset.py:200
msgid "Disk total" msgid "Disk total"
msgstr "硬盘大小" msgstr "硬盘大小"
#: assets/models/asset.py:166 #: assets/models/asset.py:201
msgid "Disk info" msgid "Disk info"
msgstr "硬盘信息" msgstr "硬盘信息"
#: assets/models/asset.py:168 assets/templates/assets/asset_detail.html:104 #: assets/models/asset.py:203 assets/templates/assets/asset_detail.html:104
msgid "OS" msgid "OS"
msgstr "操作系统" msgstr "操作系统"
#: assets/models/asset.py:169 #: assets/models/asset.py:204
msgid "OS version" msgid "OS version"
msgstr "系统版本" msgstr "系统版本"
#: assets/models/asset.py:170 #: assets/models/asset.py:205
msgid "OS arch" msgid "OS arch"
msgstr "系统架构" msgstr "系统架构"
#: assets/models/asset.py:171 #: assets/models/asset.py:206
msgid "Hostname raw" msgid "Hostname raw"
msgstr "主机名原始" msgstr "主机名原始"
#: assets/models/asset.py:173 assets/templates/assets/asset_create.html:46 #: assets/models/asset.py:208 assets/templates/assets/asset_create.html:46
#: assets/templates/assets/asset_detail.html:220 templates/_nav.html:46 #: assets/templates/assets/asset_detail.html:220 templates/_nav.html:46
msgid "Labels" msgid "Labels"
msgstr "标签管理" msgstr "标签管理"
...@@ -962,8 +989,8 @@ msgstr "ssh公钥" ...@@ -962,8 +989,8 @@ msgstr "ssh公钥"
#: assets/models/base.py:35 assets/models/gathered_user.py:20 #: assets/models/base.py:35 assets/models/gathered_user.py:20
#: assets/templates/assets/cmd_filter_detail.html:68 common/mixins/models.py:52 #: assets/templates/assets/cmd_filter_detail.html:68 common/mixins/models.py:52
#: ops/models/adhoc.py:46 #: ops/models/adhoc.py:46
#: 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:107
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:68 #: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:71
msgid "Date updated" msgid "Date updated"
msgstr "更新日期" msgstr "更新日期"
...@@ -975,7 +1002,7 @@ msgstr "带宽" ...@@ -975,7 +1002,7 @@ msgstr "带宽"
msgid "Contact" msgid "Contact"
msgstr "联系人" msgstr "联系人"
#: assets/models/cluster.py:22 users/models/user.py:462 #: assets/models/cluster.py:22 users/models/user.py:452
#: users/templates/users/user_detail.html:77 #: users/templates/users/user_detail.html:77
msgid "Phone" msgid "Phone"
msgstr "手机" msgstr "手机"
...@@ -1001,7 +1028,7 @@ msgid "Default" ...@@ -1001,7 +1028,7 @@ msgid "Default"
msgstr "默认" msgstr "默认"
#: assets/models/cluster.py:36 assets/models/label.py:14 #: assets/models/cluster.py:36 assets/models/label.py:14
#: users/models/user.py:585 #: users/models/user.py:575
msgid "System" msgid "System"
msgstr "系统" msgstr "系统"
...@@ -1092,17 +1119,15 @@ msgid "Gateway" ...@@ -1092,17 +1119,15 @@ msgid "Gateway"
msgstr "网关" msgstr "网关"
#: assets/models/gathered_user.py:16 #: assets/models/gathered_user.py:16
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:67 #: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:70
msgid "Present" msgid "Present"
msgstr "存在" msgstr "存在"
#: assets/models/gathered_user.py:17 #: assets/models/gathered_user.py:17
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:65
msgid "Date last login" msgid "Date last login"
msgstr "最后登录日期" msgstr "最后登录日期"
#: assets/models/gathered_user.py:18 #: assets/models/gathered_user.py:18
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:66
msgid "IP last login" msgid "IP last login"
msgstr "最后登录IP" msgstr "最后登录IP"
...@@ -1142,14 +1167,14 @@ msgstr "默认资产组" ...@@ -1142,14 +1167,14 @@ msgstr "默认资产组"
#: tickets/models/ticket.py:128 tickets/templates/tickets/ticket_detail.html:32 #: tickets/models/ticket.py:128 tickets/templates/tickets/ticket_detail.html:32
#: tickets/templates/tickets/ticket_list.html:34 #: tickets/templates/tickets/ticket_list.html:34
#: tickets/templates/tickets/ticket_list.html:103 users/forms.py:339 #: tickets/templates/tickets/ticket_list.html:103 users/forms.py:339
#: users/models/user.py:148 users/models/user.py:164 users/models/user.py:573 #: users/models/user.py:148 users/models/user.py:164 users/models/user.py:563
#: users/serializers/group.py:21 #: users/serializers/group.py:21
#: users/templates/users/user_asset_permission.html:55 #: users/templates/users/user_asset_permission.html:55
#: users/templates/users/user_asset_permission.html:84 #: users/templates/users/user_asset_permission.html:84
#: users/templates/users/user_group_detail.html:73 #: users/templates/users/user_group_detail.html:73
#: users/templates/users/user_group_list.html:36 users/views/profile.py:68 #: users/templates/users/user_group_list.html:36 users/views/profile.py:68
#: xpack/plugins/orgs/forms.py:28 #: xpack/plugins/orgs/forms.py:28
#: xpack/plugins/orgs/templates/orgs/org_detail.html:113 #: xpack/plugins/orgs/templates/orgs/org_detail.html:108
#: xpack/plugins/orgs/templates/orgs/org_list.html:15 #: xpack/plugins/orgs/templates/orgs/org_list.html:15
msgid "User" msgid "User"
msgstr "用户" msgstr "用户"
...@@ -1179,7 +1204,7 @@ msgstr "空" ...@@ -1179,7 +1204,7 @@ msgstr "空"
msgid "favorite" msgid "favorite"
msgstr "收藏夹" msgstr "收藏夹"
#: assets/models/node.py:452 assets/templates/assets/_node_detail_modal.html:39 #: assets/models/node.py:452
msgid "Key" msgid "Key"
msgstr "键" msgstr "键"
...@@ -1203,14 +1228,16 @@ msgstr "手动登录" ...@@ -1203,14 +1228,16 @@ msgstr "手动登录"
#: assets/views/cmd_filter.py:31 assets/views/cmd_filter.py:48 #: assets/views/cmd_filter.py:31 assets/views/cmd_filter.py:48
#: assets/views/cmd_filter.py:66 assets/views/cmd_filter.py:84 #: assets/views/cmd_filter.py:66 assets/views/cmd_filter.py:84
#: assets/views/cmd_filter.py:104 assets/views/cmd_filter.py:138 #: assets/views/cmd_filter.py:104 assets/views/cmd_filter.py:138
#: assets/views/cmd_filter.py:173 assets/views/domain.py:30 #: assets/views/cmd_filter.py:173 assets/views/domain.py:31
#: assets/views/domain.py:47 assets/views/domain.py:65 #: assets/views/domain.py:48 assets/views/domain.py:66
#: assets/views/domain.py:80 assets/views/domain.py:106 #: assets/views/domain.py:81 assets/views/domain.py:107
#: assets/views/domain.py:135 assets/views/domain.py:156 #: assets/views/domain.py:136 assets/views/domain.py:157
#: assets/views/label.py:27 assets/views/label.py:45 assets/views/label.py:73 #: assets/views/label.py:27 assets/views/label.py:45 assets/views/label.py:73
#: assets/views/system_user.py:29 assets/views/system_user.py:46 #: assets/views/platform.py:17 assets/views/platform.py:32
#: assets/views/system_user.py:63 assets/views/system_user.py:79 #: assets/views/platform.py:47 assets/views/system_user.py:29
#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:71 #: assets/views/system_user.py:46 assets/views/system_user.py:63
#: assets/views/system_user.py:79 templates/_nav.html:39
#: xpack/plugins/change_auth_plan/models.py:71
msgid "Assets" msgid "Assets"
msgstr "资产管理" msgstr "资产管理"
...@@ -1284,17 +1311,17 @@ msgstr "协议格式 {}/{}" ...@@ -1284,17 +1311,17 @@ msgstr "协议格式 {}/{}"
msgid "Protocol duplicate: {}" msgid "Protocol duplicate: {}"
msgstr "协议重复: {}" msgstr "协议重复: {}"
#: assets/serializers/asset.py:69 assets/serializers/asset.py:143 #: assets/serializers/asset.py:70 assets/serializers/asset.py:155
#: assets/serializers/asset_user.py:29 #: assets/serializers/asset_user.py:29
#: assets/templates/assets/_asset_user_list.html:23 #: assets/templates/assets/_asset_user_list.html:23
msgid "Connectivity" msgid "Connectivity"
msgstr "连接" msgstr "连接"
#: assets/serializers/asset.py:95 #: assets/serializers/asset.py:96
msgid "Hardware info" msgid "Hardware info"
msgstr "硬件信息" msgstr "硬件信息"
#: assets/serializers/asset.py:96 orgs/mixins/serializers.py:27 #: assets/serializers/asset.py:97 orgs/mixins/serializers.py:27
msgid "Org name" msgid "Org name"
msgstr "组织名称" msgstr "组织名称"
...@@ -1303,7 +1330,7 @@ msgid "Backend" ...@@ -1303,7 +1330,7 @@ msgid "Backend"
msgstr "后端" msgstr "后端"
#: assets/serializers/asset_user.py:67 users/forms.py:282 #: assets/serializers/asset_user.py:67 users/forms.py:282
#: users/models/user.py:473 users/templates/users/first_login.html:42 #: users/models/user.py:463 users/templates/users/first_login.html:42
#: users/templates/users/user_password_update.html:49 #: users/templates/users/user_password_update.html:49
#: users/templates/users/user_profile.html:69 #: users/templates/users/user_profile.html:69
#: users/templates/users/user_profile_update.html:46 #: users/templates/users/user_profile_update.html:46
...@@ -1478,7 +1505,7 @@ msgstr "资产列表" ...@@ -1478,7 +1505,7 @@ msgstr "资产列表"
#: ops/templates/ops/command_execution_create.html:124 #: ops/templates/ops/command_execution_create.html:124
#: settings/templates/settings/_ldap_list_users_modal.html:41 #: settings/templates/settings/_ldap_list_users_modal.html:41
#: users/templates/users/_granted_assets.html:7 #: users/templates/users/_granted_assets.html:7
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:66 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:62
msgid "Loading" msgid "Loading"
msgstr "加载中" msgstr "加载中"
...@@ -1509,7 +1536,6 @@ msgid "Asset user auth" ...@@ -1509,7 +1536,6 @@ msgid "Asset user auth"
msgstr "资产用户信息" msgstr "资产用户信息"
#: assets/templates/assets/_asset_user_auth_view_modal.html:54 #: assets/templates/assets/_asset_user_auth_view_modal.html:54
#: assets/templates/assets/_node_detail_modal.html:56
#: authentication/templates/authentication/login_wait_confirm.html:114 #: authentication/templates/authentication/login_wait_confirm.html:114
msgid "Copy success" msgid "Copy success"
msgstr "复制成功" msgstr "复制成功"
...@@ -1519,7 +1545,6 @@ msgid "Get auth info error" ...@@ -1519,7 +1545,6 @@ msgid "Get auth info error"
msgstr "获取认证信息错误" msgstr "获取认证信息错误"
#: assets/templates/assets/_asset_user_auth_view_modal.html:97 #: assets/templates/assets/_asset_user_auth_view_modal.html:97
#: assets/templates/assets/_node_detail_modal.html:67
#: assets/templates/assets/_user_asset_detail_modal.html:23 #: assets/templates/assets/_user_asset_detail_modal.html:23
#: authentication/templates/authentication/_access_key_modal.html:142 #: authentication/templates/authentication/_access_key_modal.html:142
#: authentication/templates/authentication/_mfa_confirm_modal.html:53 #: authentication/templates/authentication/_mfa_confirm_modal.html:53
...@@ -1540,7 +1565,7 @@ msgid "Datetime" ...@@ -1540,7 +1565,7 @@ msgid "Datetime"
msgstr "日期" msgstr "日期"
#: assets/templates/assets/_asset_user_list.html:41 #: assets/templates/assets/_asset_user_list.html:41
#: assets/templates/assets/asset_list.html:138 #: assets/templates/assets/asset_list.html:137
msgid "Test datetime: " msgid "Test datetime: "
msgstr "测试日期: " msgstr "测试日期: "
...@@ -1576,27 +1601,6 @@ msgstr "SSH端口" ...@@ -1576,27 +1601,6 @@ msgstr "SSH端口"
msgid "If use nat, set the ssh real port" msgid "If use nat, set the ssh real port"
msgstr "如果使用了nat端口映射,请设置为ssh真实监听的端口" msgstr "如果使用了nat端口映射,请设置为ssh真实监听的端口"
#: assets/templates/assets/_node_detail_modal.html:11
#: assets/templates/assets/asset_list.html:203
msgid "Node detail"
msgstr "节点详情"
#: assets/templates/assets/_node_detail_modal.html:18
#: audits/templates/audits/login_log_list.html:56
#: authentication/templates/authentication/_access_key_modal.html:30
#: ops/templates/ops/adhoc_detail.html:47
#: ops/templates/ops/adhoc_history_detail.html:47
#: ops/templates/ops/task_detail.html:54
#: terminal/templates/terminal/session_list.html:24
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:64
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:60
msgid "ID"
msgstr "ID"
#: assets/templates/assets/_node_detail_modal.html:33
msgid "Full name"
msgstr "全名"
#: assets/templates/assets/_node_tree.html:49 #: assets/templates/assets/_node_tree.html:49
msgid "Add node" msgid "Add node"
msgstr "新建节点" msgstr "新建节点"
...@@ -1630,9 +1634,9 @@ msgstr "重命名成功" ...@@ -1630,9 +1634,9 @@ msgstr "重命名成功"
#: assets/templates/assets/gateway_create_update.html:33 #: assets/templates/assets/gateway_create_update.html:33
#: perms/templates/perms/asset_permission_create_update.html:36 #: perms/templates/perms/asset_permission_create_update.html:36
#: perms/templates/perms/remote_app_permission_create_update.html:37 #: perms/templates/perms/remote_app_permission_create_update.html:37
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:41 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:39
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:27 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:23
#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:27 #: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:23
msgid "Basic" msgid "Basic"
msgstr "基本" msgstr "基本"
...@@ -1653,9 +1657,9 @@ msgstr "自动生成密钥" ...@@ -1653,9 +1657,9 @@ msgstr "自动生成密钥"
#: perms/templates/perms/asset_permission_create_update.html:51 #: perms/templates/perms/asset_permission_create_update.html:51
#: perms/templates/perms/remote_app_permission_create_update.html:51 #: perms/templates/perms/remote_app_permission_create_update.html:51
#: terminal/templates/terminal/terminal_update.html:38 #: terminal/templates/terminal/terminal_update.html:38
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:65 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:63
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:48 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:44
#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:39 #: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:35
msgid "Other" msgid "Other"
msgstr "其它" msgstr "其它"
...@@ -1681,7 +1685,7 @@ msgstr "资产列表" ...@@ -1681,7 +1685,7 @@ msgstr "资产列表"
#: assets/templates/assets/admin_user_assets.html:24 #: assets/templates/assets/admin_user_assets.html:24
#: perms/templates/perms/asset_permission_asset.html:31 #: perms/templates/perms/asset_permission_asset.html:31
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:31 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:27
msgid "Asset list of " msgid "Asset list of "
msgstr "资产列表" msgstr "资产列表"
...@@ -1706,14 +1710,14 @@ msgstr "替换资产的管理员" ...@@ -1706,14 +1710,14 @@ msgstr "替换资产的管理员"
#: assets/templates/assets/admin_user_detail.html:86 #: assets/templates/assets/admin_user_detail.html:86
#: perms/templates/perms/asset_permission_asset.html:99 #: perms/templates/perms/asset_permission_asset.html:99
#: xpack/plugins/change_auth_plan/forms.py:68 #: xpack/plugins/change_auth_plan/forms.py:68
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:99 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:95
#: xpack/plugins/gathered_user/forms.py:36 #: xpack/plugins/gathered_user/forms.py:36
msgid "Select nodes" msgid "Select nodes"
msgstr "选择节点" msgstr "选择节点"
#: assets/templates/assets/admin_user_detail.html:95 #: assets/templates/assets/admin_user_detail.html:95
#: assets/templates/assets/asset_detail.html:200 #: assets/templates/assets/asset_detail.html:200
#: assets/templates/assets/asset_list.html:427 #: assets/templates/assets/asset_list.html:424
#: assets/templates/assets/cmd_filter_detail.html:101 #: assets/templates/assets/cmd_filter_detail.html:101
#: assets/templates/assets/system_user_assets.html:101 #: assets/templates/assets/system_user_assets.html:101
#: assets/templates/assets/system_user_detail.html:177 #: assets/templates/assets/system_user_detail.html:177
...@@ -1727,12 +1731,12 @@ msgstr "选择节点" ...@@ -1727,12 +1731,12 @@ msgstr "选择节点"
#: users/templates/users/user_detail.html:548 #: users/templates/users/user_detail.html:548
#: users/templates/users/user_group_create_update.html:28 #: users/templates/users/user_group_create_update.html:28
#: users/templates/users/user_group_list.html:120 #: users/templates/users/user_group_list.html:120
#: users/templates/users/user_list.html:276 #: users/templates/users/user_list.html:256
#: xpack/plugins/cloud/templates/cloud/account_create_update.html:34 #: xpack/plugins/cloud/templates/cloud/account_create_update.html:30
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:54 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:50
#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:45 #: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:41
#: xpack/plugins/interface/templates/interface/interface.html:103 #: xpack/plugins/interface/templates/interface/interface.html:103
#: xpack/plugins/orgs/templates/orgs/org_create_update.html:34 #: xpack/plugins/orgs/templates/orgs/org_create_update.html:30
msgid "Confirm" msgid "Confirm"
msgstr "确认" msgstr "确认"
...@@ -1755,8 +1759,8 @@ msgstr "Jumpserver 使用该用户来 `推送系统用户`、`获取资产硬件 ...@@ -1755,8 +1759,8 @@ msgstr "Jumpserver 使用该用户来 `推送系统用户`、`获取资产硬件
#: audits/templates/audits/login_log_list.html:91 #: audits/templates/audits/login_log_list.html:91
#: users/templates/users/user_group_list.html:10 #: users/templates/users/user_group_list.html:10
#: users/templates/users/user_list.html:10 #: users/templates/users/user_list.html:10
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:49 #: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:54
#: xpack/plugins/vault/templates/vault/vault.html:47 #: xpack/plugins/vault/templates/vault/vault.html:53
msgid "Export" msgid "Export"
msgstr "导出" msgstr "导出"
...@@ -1766,8 +1770,8 @@ msgstr "导出" ...@@ -1766,8 +1770,8 @@ msgstr "导出"
#: settings/templates/settings/_ldap_list_users_modal.html:172 #: settings/templates/settings/_ldap_list_users_modal.html:172
#: users/templates/users/user_group_list.html:15 #: users/templates/users/user_group_list.html:15
#: users/templates/users/user_list.html:15 #: users/templates/users/user_list.html:15
#: xpack/plugins/license/templates/license/license_detail.html:101 #: xpack/plugins/license/templates/license/license_detail.html:110
#: xpack/plugins/vault/templates/vault/vault.html:52 #: xpack/plugins/vault/templates/vault/vault.html:58
msgid "Import" msgid "Import"
msgstr "导入" msgstr "导入"
...@@ -1778,15 +1782,15 @@ msgstr "创建管理用户" ...@@ -1778,15 +1782,15 @@ msgstr "创建管理用户"
#: assets/templates/assets/admin_user_list.html:123 #: assets/templates/assets/admin_user_list.html:123
#: assets/templates/assets/admin_user_list.html:154 #: assets/templates/assets/admin_user_list.html:154
#: assets/templates/assets/asset_list.html:307 #: assets/templates/assets/asset_list.html:304
#: assets/templates/assets/asset_list.html:344 #: assets/templates/assets/asset_list.html:341
#: assets/templates/assets/system_user_list.html:186 #: assets/templates/assets/system_user_list.html:186
#: assets/templates/assets/system_user_list.html:217 #: assets/templates/assets/system_user_list.html:217
#: users/templates/users/user_group_list.html:164 #: users/templates/users/user_group_list.html:164
#: users/templates/users/user_group_list.html:195 #: users/templates/users/user_group_list.html:195
#: users/templates/users/user_list.html:184 #: users/templates/users/user_list.html:165
#: users/templates/users/user_list.html:216 #: users/templates/users/user_list.html:197
#: xpack/plugins/vault/templates/vault/vault.html:200 #: xpack/plugins/vault/templates/vault/vault.html:222
msgid "Please select file" msgid "Please select file"
msgstr "选择文件" msgstr "选择文件"
...@@ -1804,9 +1808,9 @@ msgstr "资产用户" ...@@ -1804,9 +1808,9 @@ msgstr "资产用户"
#: terminal/templates/terminal/session_detail.html:85 #: terminal/templates/terminal/session_detail.html:85
#: users/templates/users/user_detail.html:141 #: users/templates/users/user_detail.html:141
#: users/templates/users/user_profile.html:150 #: users/templates/users/user_profile.html:150
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:128 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:126
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:132 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:129
#: xpack/plugins/license/templates/license/license_detail.html:93 #: xpack/plugins/license/templates/license/license_detail.html:102
msgid "Quick modify" msgid "Quick modify"
msgstr "快速修改" msgstr "快速修改"
...@@ -1875,12 +1879,12 @@ msgid "Hardware" ...@@ -1875,12 +1879,12 @@ msgid "Hardware"
msgstr "硬件" msgstr "硬件"
#: assets/templates/assets/asset_list.html:109 #: assets/templates/assets/asset_list.html:109
#: users/templates/users/user_list.html:51 #: users/templates/users/user_list.html:50
msgid "Delete selected" msgid "Delete selected"
msgstr "批量删除" msgstr "批量删除"
#: assets/templates/assets/asset_list.html:110 #: assets/templates/assets/asset_list.html:110
#: users/templates/users/user_list.html:55 #: users/templates/users/user_list.html:51
msgid "Update selected" msgid "Update selected"
msgstr "批量更新" msgstr "批量更新"
...@@ -1889,75 +1893,75 @@ msgid "Remove from this node" ...@@ -1889,75 +1893,75 @@ msgid "Remove from this node"
msgstr "从节点移除" msgstr "从节点移除"
#: assets/templates/assets/asset_list.html:112 #: assets/templates/assets/asset_list.html:112
#: users/templates/users/user_list.html:56 #: users/templates/users/user_list.html:52
msgid "Deactive selected" msgid "Deactive selected"
msgstr "禁用所选" msgstr "禁用所选"
#: assets/templates/assets/asset_list.html:113 #: assets/templates/assets/asset_list.html:113
#: users/templates/users/user_list.html:57 #: users/templates/users/user_list.html:53
msgid "Active selected" msgid "Active selected"
msgstr "激活所选" msgstr "激活所选"
#: assets/templates/assets/asset_list.html:194 #: assets/templates/assets/asset_list.html:193
msgid "Add assets to node" msgid "Add assets to node"
msgstr "添加资产到节点" msgstr "添加资产到节点"
#: assets/templates/assets/asset_list.html:195 #: assets/templates/assets/asset_list.html:194
msgid "Move assets to node" msgid "Move assets to node"
msgstr "移动资产到节点" msgstr "移动资产到节点"
#: assets/templates/assets/asset_list.html:197 #: assets/templates/assets/asset_list.html:196
msgid "Refresh node hardware info" msgid "Refresh node hardware info"
msgstr "更新节点资产硬件信息" msgstr "更新节点资产硬件信息"
#: assets/templates/assets/asset_list.html:198 #: assets/templates/assets/asset_list.html:197
msgid "Test node connective" msgid "Test node connective"
msgstr "测试节点资产可连接性" msgstr "测试节点资产可连接性"
#: assets/templates/assets/asset_list.html:200 #: assets/templates/assets/asset_list.html:199
msgid "Display only current node assets" msgid "Display only current node assets"
msgstr "仅显示当前节点资产" msgstr "仅显示当前节点资产"
#: assets/templates/assets/asset_list.html:201 #: assets/templates/assets/asset_list.html:200
msgid "Displays all child node assets" msgid "Displays all child node assets"
msgstr "显示所有子节点资产" msgstr "显示所有子节点资产"
#: assets/templates/assets/asset_list.html:421 #: assets/templates/assets/asset_list.html:418
#: assets/templates/assets/system_user_list.html:127 #: assets/templates/assets/system_user_list.html:127
#: users/templates/users/user_detail.html:448 #: users/templates/users/user_detail.html:448
#: users/templates/users/user_detail.html:474 #: users/templates/users/user_detail.html:474
#: users/templates/users/user_detail.html:542 #: users/templates/users/user_detail.html:542
#: users/templates/users/user_group_list.html:114 #: users/templates/users/user_group_list.html:114
#: users/templates/users/user_list.html:270 #: users/templates/users/user_list.html:250
#: 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:422 #: assets/templates/assets/asset_list.html:419
msgid "This will delete the selected assets !!!" msgid "This will delete the selected assets !!!"
msgstr "删除选择资产" msgstr "删除选择资产"
#: assets/templates/assets/asset_list.html:425 #: assets/templates/assets/asset_list.html:422
#: assets/templates/assets/system_user_list.html:131 #: assets/templates/assets/system_user_list.html:131
#: users/templates/users/user_detail.html:452 #: users/templates/users/user_detail.html:452
#: users/templates/users/user_detail.html:478 #: users/templates/users/user_detail.html:478
#: users/templates/users/user_detail.html:546 #: users/templates/users/user_detail.html:546
#: users/templates/users/user_group_list.html:118 #: users/templates/users/user_group_list.html:118
#: users/templates/users/user_list.html:274 #: users/templates/users/user_list.html:254
#: xpack/plugins/interface/templates/interface/interface.html:101 #: xpack/plugins/interface/templates/interface/interface.html:101
msgid "Cancel" msgid "Cancel"
msgstr "取消" msgstr "取消"
#: assets/templates/assets/asset_list.html:436 #: assets/templates/assets/asset_list.html:433
msgid "Asset Deleted." msgid "Asset Deleted."
msgstr "已被删除" msgstr "已被删除"
#: assets/templates/assets/asset_list.html:437 #: assets/templates/assets/asset_list.html:434
#: assets/templates/assets/asset_list.html:445 #: assets/templates/assets/asset_list.html:442
msgid "Asset Delete" msgid "Asset Delete"
msgstr "删除" msgstr "删除"
#: assets/templates/assets/asset_list.html:444 #: assets/templates/assets/asset_list.html:441
msgid "Asset Deleting failed." msgid "Asset Deleting failed."
msgstr "删除失败" msgstr "删除失败"
...@@ -2034,14 +2038,14 @@ msgid "Gateway list" ...@@ -2034,14 +2038,14 @@ msgid "Gateway list"
msgstr "网关列表" msgstr "网关列表"
#: assets/templates/assets/domain_gateway_list.html:51 #: assets/templates/assets/domain_gateway_list.html:51
#: assets/views/domain.py:136 #: assets/views/domain.py:137
msgid "Create gateway" msgid "Create gateway"
msgstr "创建网关" msgstr "创建网关"
#: assets/templates/assets/domain_gateway_list.html:94 #: assets/templates/assets/domain_gateway_list.html:94
#: assets/templates/assets/domain_gateway_list.html:96 #: assets/templates/assets/domain_gateway_list.html:96
#: settings/templates/settings/email_setting.html:64 #: settings/templates/settings/email_setting.html:45
#: settings/templates/settings/ldap_setting.html:65 #: settings/templates/settings/ldap_setting.html:46
msgid "Test connection" msgid "Test connection"
msgstr "测试连接" msgstr "测试连接"
...@@ -2062,7 +2066,7 @@ msgstr "" ...@@ -2062,7 +2066,7 @@ msgstr ""
msgid "JMS => Domain gateway => Target assets" msgid "JMS => Domain gateway => Target assets"
msgstr "JMS => 网域网关 => 目标资产" msgstr "JMS => 网域网关 => 目标资产"
#: assets/templates/assets/domain_list.html:13 assets/views/domain.py:48 #: assets/templates/assets/domain_list.html:13 assets/views/domain.py:49
msgid "Create domain" msgid "Create domain"
msgstr "创建网域" msgstr "创建网域"
...@@ -2070,6 +2074,10 @@ msgstr "创建网域" ...@@ -2070,6 +2074,10 @@ msgstr "创建网域"
msgid "Create label" msgid "Create label"
msgstr "创建标签" msgstr "创建标签"
#: assets/templates/assets/platform_list.html:8 assets/views/platform.py:33
msgid "Create platform"
msgstr "创建系统平台"
#: assets/templates/assets/system_user_assets.html:35 #: assets/templates/assets/system_user_assets.html:35
msgid "Assets of " msgid "Assets of "
msgstr "资产" msgstr "资产"
...@@ -2194,23 +2202,23 @@ msgstr "创建命令过滤器规则" ...@@ -2194,23 +2202,23 @@ msgstr "创建命令过滤器规则"
msgid "Update command filter rule" msgid "Update command filter rule"
msgstr "更新命令过滤器规则" msgstr "更新命令过滤器规则"
#: assets/views/domain.py:31 templates/_nav.html:43 #: assets/views/domain.py:32 templates/_nav.html:43
msgid "Domain list" msgid "Domain list"
msgstr "网域列表" msgstr "网域列表"
#: assets/views/domain.py:66 #: assets/views/domain.py:67
msgid "Update domain" msgid "Update domain"
msgstr "更新网域" msgstr "更新网域"
#: assets/views/domain.py:81 #: assets/views/domain.py:82
msgid "Domain detail" msgid "Domain detail"
msgstr "网域详情" msgstr "网域详情"
#: assets/views/domain.py:107 #: assets/views/domain.py:108
msgid "Domain gateway list" msgid "Domain gateway list"
msgstr "域网关列表" msgstr "域网关列表"
#: assets/views/domain.py:157 #: assets/views/domain.py:158
msgid "Update gateway" msgid "Update gateway"
msgstr "创建网关" msgstr "创建网关"
...@@ -2226,6 +2234,14 @@ msgstr "提示: 请避免使用内部预留标签名: {}" ...@@ -2226,6 +2234,14 @@ msgstr "提示: 请避免使用内部预留标签名: {}"
msgid "Update label" msgid "Update label"
msgstr "更新标签" msgstr "更新标签"
#: assets/views/platform.py:18
msgid "Platform list"
msgstr "平台列表"
#: assets/views/platform.py:48
msgid "Update platform"
msgstr "更新系统平台"
#: assets/views/system_user.py:30 #: assets/views/system_user.py:30
msgid "System user list" msgid "System user list"
msgstr "系统用户列表" msgstr "系统用户列表"
...@@ -2273,7 +2289,7 @@ msgstr "成功" ...@@ -2273,7 +2289,7 @@ msgstr "成功"
#: audits/models.py:33 #: audits/models.py:33
#: authentication/templates/authentication/_access_key_modal.html:22 #: authentication/templates/authentication/_access_key_modal.html:22
#: xpack/plugins/vault/templates/vault/vault.html:38 #: xpack/plugins/vault/templates/vault/vault.html:44
msgid "Create" msgid "Create"
msgstr "创建" msgstr "创建"
...@@ -2326,7 +2342,7 @@ msgstr "Agent" ...@@ -2326,7 +2342,7 @@ msgstr "Agent"
#: audits/models.py:86 audits/templates/audits/login_log_list.html:62 #: audits/models.py:86 audits/templates/audits/login_log_list.html:62
#: authentication/templates/authentication/_mfa_confirm_modal.html:14 #: authentication/templates/authentication/_mfa_confirm_modal.html:14
#: users/forms.py:194 users/models/user.py:465 #: users/forms.py:194 users/models/user.py:455
#: users/templates/users/first_login.html:45 #: users/templates/users/first_login.html:45
msgid "MFA" msgid "MFA"
msgstr "MFA" msgstr "MFA"
...@@ -2335,7 +2351,7 @@ msgstr "MFA" ...@@ -2335,7 +2351,7 @@ msgstr "MFA"
#: xpack/plugins/change_auth_plan/models.py:423 #: xpack/plugins/change_auth_plan/models.py:423
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:15 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:15
#: xpack/plugins/cloud/models.py:278 #: xpack/plugins/cloud/models.py:278
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:69 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:64
msgid "Reason" msgid "Reason"
msgstr "原因" msgstr "原因"
...@@ -2344,8 +2360,8 @@ msgstr "原因" ...@@ -2344,8 +2360,8 @@ msgstr "原因"
#: tickets/templates/tickets/ticket_list.html:36 #: tickets/templates/tickets/ticket_list.html:36
#: tickets/templates/tickets/ticket_list.html:104 #: tickets/templates/tickets/ticket_list.html:104
#: xpack/plugins/cloud/models.py:275 xpack/plugins/cloud/models.py:310 #: xpack/plugins/cloud/models.py:275 xpack/plugins/cloud/models.py:310
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:70 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:65
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:65 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:62
msgid "Status" msgid "Status"
msgstr "状态" msgstr "状态"
...@@ -2365,7 +2381,7 @@ msgstr "登录日期" ...@@ -2365,7 +2381,7 @@ msgstr "登录日期"
#: xpack/plugins/change_auth_plan/models.py:426 #: xpack/plugins/change_auth_plan/models.py:426
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:59 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:59
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:17 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:17
#: xpack/plugins/gathered_user/models.py:140 #: xpack/plugins/gathered_user/models.py:143
msgid "Date start" msgid "Date start"
msgstr "开始日期" msgstr "开始日期"
...@@ -2382,11 +2398,22 @@ msgstr "选择用户" ...@@ -2382,11 +2398,22 @@ msgstr "选择用户"
#: ops/templates/ops/command_execution_list.html:49 #: ops/templates/ops/command_execution_list.html:49
#: ops/templates/ops/command_execution_list.html:54 #: ops/templates/ops/command_execution_list.html:54
#: templates/_base_list.html:37 templates/_user_profile.html:23 #: templates/_base_list.html:37 templates/_user_profile.html:23
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:52 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:47
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:48 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:45
msgid "Search" msgid "Search"
msgstr "搜索" msgstr "搜索"
#: audits/templates/audits/login_log_list.html:56
#: authentication/templates/authentication/_access_key_modal.html:30
#: ops/templates/ops/adhoc_detail.html:47
#: ops/templates/ops/adhoc_history_detail.html:47
#: ops/templates/ops/task_detail.html:54
#: terminal/templates/terminal/session_list.html:24
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:59
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:57
msgid "ID"
msgstr "ID"
#: audits/templates/audits/login_log_list.html:59 #: audits/templates/audits/login_log_list.html:59
msgid "UA" msgid "UA"
msgstr "Agent" msgstr "Agent"
...@@ -2581,14 +2608,14 @@ msgid "Show" ...@@ -2581,14 +2608,14 @@ msgid "Show"
msgstr "显示" msgstr "显示"
#: authentication/templates/authentication/_access_key_modal.html:66 #: authentication/templates/authentication/_access_key_modal.html:66
#: users/models/user.py:365 users/templates/users/user_profile.html:94 #: users/models/user.py:355 users/templates/users/user_profile.html:94
#: users/templates/users/user_profile.html:163 #: users/templates/users/user_profile.html:163
#: users/templates/users/user_profile.html:166 #: users/templates/users/user_profile.html:166
msgid "Disable" msgid "Disable"
msgstr "禁用" msgstr "禁用"
#: authentication/templates/authentication/_access_key_modal.html:67 #: authentication/templates/authentication/_access_key_modal.html:67
#: users/models/user.py:366 users/templates/users/user_profile.html:92 #: users/models/user.py:356 users/templates/users/user_profile.html:92
#: users/templates/users/user_profile.html:170 #: users/templates/users/user_profile.html:170
msgid "Enable" msgid "Enable"
msgstr "启用" msgstr "启用"
...@@ -2804,8 +2831,10 @@ msgid "discard time" ...@@ -2804,8 +2831,10 @@ msgid "discard time"
msgstr "" msgstr ""
#: common/utils/ipip/utils.py:15 #: common/utils/ipip/utils.py:15
#, fuzzy
#| msgid "Invalid file."
msgid "Invalid ip" msgid "Invalid ip"
msgstr "无效 IP" msgstr "文件不合法"
#: common/validators.py:11 #: common/validators.py:11
msgid "Special char not allowed" msgid "Special char not allowed"
...@@ -2908,8 +2937,8 @@ msgid "Become" ...@@ -2908,8 +2937,8 @@ msgid "Become"
msgstr "Become" msgstr "Become"
#: ops/models/adhoc.py:191 users/templates/users/user_group_detail.html:54 #: ops/models/adhoc.py:191 users/templates/users/user_group_detail.html:54
#: xpack/plugins/cloud/templates/cloud/account_detail.html:62 #: xpack/plugins/cloud/templates/cloud/account_detail.html:59
#: xpack/plugins/orgs/templates/orgs/org_detail.html:56 #: xpack/plugins/orgs/templates/orgs/org_detail.html:51
msgid "Create by" msgid "Create by"
msgstr "创建者" msgstr "创建者"
...@@ -2935,7 +2964,7 @@ msgstr "完成时间" ...@@ -2935,7 +2964,7 @@ msgstr "完成时间"
#: xpack/plugins/change_auth_plan/models.py:429 #: xpack/plugins/change_auth_plan/models.py:429
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:58 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:58
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:16 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:16
#: xpack/plugins/gathered_user/models.py:143 #: xpack/plugins/gathered_user/models.py:146
msgid "Time" msgid "Time"
msgstr "时间" msgstr "时间"
...@@ -2978,8 +3007,10 @@ msgid "Task end" ...@@ -2978,8 +3007,10 @@ msgid "Task end"
msgstr "任务结束" msgstr "任务结束"
#: ops/tasks.py:63 #: ops/tasks.py:63
#, fuzzy
#| msgid "Sync task history"
msgid "Clean task history period" msgid "Clean task history period"
msgstr "定期清除任务历史" msgstr "同步历史列表"
#: ops/tasks.py:76 #: ops/tasks.py:76
msgid "Clean celery log period" msgid "Clean celery log period"
...@@ -3168,9 +3199,9 @@ msgid "Versions" ...@@ -3168,9 +3199,9 @@ msgid "Versions"
msgstr "版本" msgstr "版本"
#: ops/templates/ops/task_list.html:68 #: ops/templates/ops/task_list.html:68
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:137 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:135
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:54 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:54
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:141 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:138
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:55 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:55
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:44 #: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:44
msgid "Run" msgid "Run"
...@@ -3235,7 +3266,7 @@ msgstr "提示:RDP 协议不支持单独控制上传或下载文件" ...@@ -3235,7 +3266,7 @@ msgstr "提示:RDP 协议不支持单独控制上传或下载文件"
#: perms/templates/perms/asset_permission_list.html:206 #: perms/templates/perms/asset_permission_list.html:206
#: perms/templates/perms/remote_app_permission_list.html:16 #: perms/templates/perms/remote_app_permission_list.html:16
#: templates/_nav.html:21 users/forms.py:313 users/models/group.py:26 #: templates/_nav.html:21 users/forms.py:313 users/models/group.py:26
#: users/models/user.py:449 users/templates/users/_select_user_modal.html:16 #: users/models/user.py:439 users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_asset_permission.html:56 #: users/templates/users/user_asset_permission.html:56
#: users/templates/users/user_asset_permission.html:87 #: users/templates/users/user_asset_permission.html:87
#: users/templates/users/user_detail.html:222 #: users/templates/users/user_detail.html:222
...@@ -3283,7 +3314,7 @@ msgstr "资产授权" ...@@ -3283,7 +3314,7 @@ msgstr "资产授权"
#: perms/models/base.py:53 #: perms/models/base.py:53
#: perms/templates/perms/asset_permission_detail.html:85 #: perms/templates/perms/asset_permission_detail.html:85
#: perms/templates/perms/remote_app_permission_detail.html:77 #: perms/templates/perms/remote_app_permission_detail.html:77
#: users/models/user.py:481 users/templates/users/user_detail.html:108 #: users/models/user.py:471 users/templates/users/user_detail.html:108
#: users/templates/users/user_profile.html:120 #: users/templates/users/user_profile.html:120
msgid "Date expired" msgid "Date expired"
msgstr "失效日期" msgstr "失效日期"
...@@ -3304,8 +3335,8 @@ msgstr "用户或用户组" ...@@ -3304,8 +3335,8 @@ msgstr "用户或用户组"
#: perms/templates/perms/asset_permission_asset.html:23 #: perms/templates/perms/asset_permission_asset.html:23
#: perms/templates/perms/asset_permission_detail.html:22 #: perms/templates/perms/asset_permission_detail.html:22
#: perms/templates/perms/asset_permission_user.html:23 #: perms/templates/perms/asset_permission_user.html:23
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:20 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:16
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:23 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:21
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:20 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:20
msgid "Assets and node" msgid "Assets and node"
msgstr "资产或节点" msgstr "资产或节点"
...@@ -3323,9 +3354,9 @@ msgstr "添加资产" ...@@ -3323,9 +3354,9 @@ msgstr "添加资产"
#: perms/templates/perms/remote_app_permission_user.html:92 #: perms/templates/perms/remote_app_permission_user.html:92
#: perms/templates/perms/remote_app_permission_user.html:120 #: perms/templates/perms/remote_app_permission_user.html:120
#: users/templates/users/user_group_detail.html:87 #: users/templates/users/user_group_detail.html:87
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:80 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:76
#: xpack/plugins/orgs/templates/orgs/org_detail.html:93 #: xpack/plugins/orgs/templates/orgs/org_detail.html:88
#: xpack/plugins/orgs/templates/orgs/org_detail.html:130 #: xpack/plugins/orgs/templates/orgs/org_detail.html:125
msgid "Add" msgid "Add"
msgstr "添加" msgstr "添加"
...@@ -3335,7 +3366,7 @@ msgstr "添加节点" ...@@ -3335,7 +3366,7 @@ msgstr "添加节点"
#: perms/templates/perms/asset_permission_asset.html:105 #: perms/templates/perms/asset_permission_asset.html:105
#: users/templates/users/user_detail.html:239 #: users/templates/users/user_detail.html:239
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:105 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:101
msgid "Join" msgid "Join"
msgstr "加入" msgstr "加入"
...@@ -3351,7 +3382,7 @@ msgstr "有效期" ...@@ -3351,7 +3382,7 @@ msgstr "有效期"
#: perms/templates/perms/asset_permission_detail.html:61 #: perms/templates/perms/asset_permission_detail.html:61
#: perms/templates/perms/remote_app_permission_detail.html:61 #: perms/templates/perms/remote_app_permission_detail.html:61
#: xpack/plugins/license/templates/license/license_detail.html:67 #: xpack/plugins/license/templates/license/license_detail.html:76
msgid "User count" msgid "User count"
msgstr "用户数量" msgstr "用户数量"
...@@ -3361,7 +3392,7 @@ msgid "User group count" ...@@ -3361,7 +3392,7 @@ msgid "User group count"
msgstr "用户组数量" msgstr "用户组数量"
#: perms/templates/perms/asset_permission_detail.html:69 #: perms/templates/perms/asset_permission_detail.html:69
#: xpack/plugins/license/templates/license/license_detail.html:63 #: xpack/plugins/license/templates/license/license_detail.html:72
msgid "Asset count" msgid "Asset count"
msgstr "资产数量" msgstr "资产数量"
...@@ -3388,7 +3419,7 @@ msgstr "刷新授权缓存" ...@@ -3388,7 +3419,7 @@ msgstr "刷新授权缓存"
#: users/templates/users/user_asset_permission.html:60 #: users/templates/users/user_asset_permission.html:60
#: users/templates/users/user_asset_permission.html:175 #: users/templates/users/user_asset_permission.html:175
#: users/templates/users/user_list.html:40 xpack/plugins/cloud/models.py:74 #: users/templates/users/user_list.html:40 xpack/plugins/cloud/models.py:74
#: xpack/plugins/cloud/templates/cloud/account_detail.html:58 #: xpack/plugins/cloud/templates/cloud/account_detail.html:55
#: xpack/plugins/cloud/templates/cloud/account_list.html:14 #: xpack/plugins/cloud/templates/cloud/account_list.html:14
msgid "Validity" msgid "Validity"
msgstr "有效" msgstr "有效"
...@@ -3858,7 +3889,7 @@ msgid "Refresh cache" ...@@ -3858,7 +3889,7 @@ msgid "Refresh cache"
msgstr "刷新缓存" msgstr "刷新缓存"
#: settings/templates/settings/_ldap_list_users_modal.html:33 #: settings/templates/settings/_ldap_list_users_modal.html:33
#: users/models/user.py:445 users/templates/users/user_detail.html:72 #: users/models/user.py:435 users/templates/users/user_detail.html:72
#: users/templates/users/user_profile.html:59 #: users/templates/users/user_profile.html:59
msgid "Email" msgid "Email"
msgstr "邮件" msgstr "邮件"
...@@ -3872,71 +3903,41 @@ msgid "" ...@@ -3872,71 +3903,41 @@ msgid ""
"User is not currently selected, please check the user you want to import" "User is not currently selected, please check the user you want to import"
msgstr "当前无勾选用户,请勾选你想要导入的用户" msgstr "当前无勾选用户,请勾选你想要导入的用户"
#: settings/templates/settings/basic_setting.html:15 #: settings/templates/settings/_setting_tabs.html:4
#: settings/templates/settings/email_content_setting.html:15 #: settings/templates/settings/terminal_setting.html:31 settings/views.py:20
#: settings/templates/settings/email_setting.html:15
#: settings/templates/settings/ldap_setting.html:15
#: settings/templates/settings/security_setting.html:15
#: settings/templates/settings/terminal_setting.html:21
#: settings/templates/settings/terminal_setting.html:54 settings/views.py:20
msgid "Basic setting" msgid "Basic setting"
msgstr "基本设置" msgstr "基本设置"
#: settings/templates/settings/basic_setting.html:18 #: settings/templates/settings/_setting_tabs.html:7 settings/views.py:47
#: settings/templates/settings/email_content_setting.html:18
#: settings/templates/settings/email_setting.html:18
#: settings/templates/settings/ldap_setting.html:18
#: settings/templates/settings/security_setting.html:18
#: settings/templates/settings/terminal_setting.html:25 settings/views.py:47
msgid "Email setting" msgid "Email setting"
msgstr "邮件设置" msgstr "邮件设置"
#: settings/templates/settings/basic_setting.html:21 #: settings/templates/settings/_setting_tabs.html:10 settings/views.py:162
#: settings/templates/settings/email_content_setting.html:21
#: settings/templates/settings/email_setting.html:21
#: settings/templates/settings/ldap_setting.html:21
#: settings/templates/settings/security_setting.html:21
#: settings/templates/settings/terminal_setting.html:28 settings/views.py:162
msgid "Email content setting" msgid "Email content setting"
msgstr "邮件内容设置" msgstr "邮件内容设置"
#: settings/templates/settings/basic_setting.html:24 #: settings/templates/settings/_setting_tabs.html:13 settings/views.py:74
#: settings/templates/settings/email_content_setting.html:24
#: settings/templates/settings/email_setting.html:24
#: settings/templates/settings/ldap_setting.html:24
#: settings/templates/settings/security_setting.html:24
#: settings/templates/settings/terminal_setting.html:32 settings/views.py:74
msgid "LDAP setting" msgid "LDAP setting"
msgstr "LDAP设置" msgstr "LDAP设置"
#: settings/templates/settings/basic_setting.html:27 #: settings/templates/settings/_setting_tabs.html:16 settings/views.py:106
#: settings/templates/settings/email_content_setting.html:27
#: settings/templates/settings/email_setting.html:27
#: settings/templates/settings/ldap_setting.html:27
#: settings/templates/settings/security_setting.html:27
#: settings/templates/settings/terminal_setting.html:36 settings/views.py:106
msgid "Terminal setting" msgid "Terminal setting"
msgstr "终端设置" msgstr "终端设置"
#: settings/templates/settings/basic_setting.html:30 #: settings/templates/settings/_setting_tabs.html:19
#: settings/templates/settings/email_content_setting.html:30 #: settings/templates/settings/security_setting.html:26 settings/views.py:135
#: settings/templates/settings/email_setting.html:30
#: settings/templates/settings/ldap_setting.html:30
#: settings/templates/settings/security_setting.html:30
#: settings/templates/settings/security_setting.html:45
#: settings/templates/settings/terminal_setting.html:39 settings/views.py:135
msgid "Security setting" msgid "Security setting"
msgstr "安全设置" msgstr "安全设置"
#: settings/templates/settings/email_content_setting.html:45 #: settings/templates/settings/email_content_setting.html:26
msgid "Create User setting" msgid "Create User setting"
msgstr "创建用户设置" msgstr "创建用户设置"
#: settings/templates/settings/ldap_setting.html:66 #: settings/templates/settings/ldap_setting.html:47
msgid "Bulk import" msgid "Bulk import"
msgstr "一键导入" msgstr "一键导入"
#: settings/templates/settings/security_setting.html:49 #: settings/templates/settings/security_setting.html:30
msgid "Password check rule" msgid "Password check rule"
msgstr "密码校验规则" msgstr "密码校验规则"
...@@ -4428,8 +4429,8 @@ msgid "" ...@@ -4428,8 +4429,8 @@ msgid ""
msgstr "" msgstr ""
#: terminal/forms/storage.py:143 xpack/plugins/cloud/models.py:304 #: terminal/forms/storage.py:143 xpack/plugins/cloud/models.py:304
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:109 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:106
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:62 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:59
msgid "Region" msgid "Region"
msgstr "地域" msgstr "地域"
...@@ -4710,10 +4711,6 @@ msgstr "标题" ...@@ -4710,10 +4711,6 @@ msgstr "标题"
msgid "Body" msgid "Body"
msgstr "内容" msgstr "内容"
#: tickets/models/ticket.py:38
msgid "Meta"
msgstr ""
#: tickets/models/ticket.py:39 tickets/templates/tickets/ticket_detail.html:51 #: tickets/models/ticket.py:39 tickets/templates/tickets/ticket_detail.html:51
msgid "Assignee" msgid "Assignee"
msgstr "处理人" msgstr "处理人"
...@@ -4827,11 +4824,11 @@ msgstr "工单列表" ...@@ -4827,11 +4824,11 @@ msgstr "工单列表"
msgid "Ticket detail" msgid "Ticket detail"
msgstr "工单详情" msgstr "工单详情"
#: users/api/user.py:180 #: users/api/user.py:174
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:47 users/models/user.py:453 #: users/forms.py:47 users/models/user.py:443
#: users/templates/users/_select_user_modal.html:15 #: users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:88 #: users/templates/users/user_detail.html:88
#: users/templates/users/user_list.html:37 #: users/templates/users/user_list.html:37
...@@ -4839,7 +4836,7 @@ msgstr "不能再该页面重置MFA, 请去个人信息页面重置" ...@@ -4839,7 +4836,7 @@ msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
msgid "Role" msgid "Role"
msgstr "角色" msgstr "角色"
#: users/forms.py:51 users/models/user.py:488 #: users/forms.py:51 users/models/user.py:478
#: users/templates/users/user_detail.html:104 #: users/templates/users/user_detail.html:104
#: users/templates/users/user_list.html:39 #: users/templates/users/user_list.html:39
#: users/templates/users/user_profile.html:102 #: users/templates/users/user_profile.html:102
...@@ -4884,8 +4881,8 @@ msgid "Set password" ...@@ -4884,8 +4881,8 @@ msgid "Set password"
msgstr "设置密码" msgstr "设置密码"
#: users/forms.py:152 xpack/plugins/change_auth_plan/models.py:89 #: users/forms.py:152 xpack/plugins/change_auth_plan/models.py:89
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:49 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:47
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:69 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:67
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:16 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:16
msgid "Password strategy" msgid "Password strategy"
...@@ -4957,7 +4954,7 @@ msgstr "选择用户" ...@@ -4957,7 +4954,7 @@ msgstr "选择用户"
msgid "User auth from {}, go there change password" msgid "User auth from {}, go there change password"
msgstr "用户认证源来自 {}, 请去相应系统修改密码" msgstr "用户认证源来自 {}, 请去相应系统修改密码"
#: users/models/user.py:147 users/models/user.py:581 #: users/models/user.py:147 users/models/user.py:571
msgid "Administrator" msgid "Administrator"
msgstr "管理员" msgstr "管理员"
...@@ -4978,27 +4975,27 @@ msgstr "组织管理员" ...@@ -4978,27 +4975,27 @@ msgstr "组织管理员"
msgid "Org auditor" msgid "Org auditor"
msgstr "组织审计员" msgstr "组织审计员"
#: users/models/user.py:367 users/templates/users/user_profile.html:90 #: users/models/user.py:357 users/templates/users/user_profile.html:90
msgid "Force enable" msgid "Force enable"
msgstr "强制启用" msgstr "强制启用"
#: users/models/user.py:433 #: users/models/user.py:423
msgid "Local" msgid "Local"
msgstr "数据库" msgstr "数据库"
#: users/models/user.py:456 #: users/models/user.py:446
msgid "Avatar" msgid "Avatar"
msgstr "头像" msgstr "头像"
#: users/models/user.py:459 users/templates/users/user_detail.html:83 #: users/models/user.py:449 users/templates/users/user_detail.html:83
msgid "Wechat" msgid "Wechat"
msgstr "微信" msgstr "微信"
#: users/models/user.py:492 #: users/models/user.py:482
msgid "Date password last updated" msgid "Date password last updated"
msgstr "最后更新密码日期" msgstr "最后更新密码日期"
#: users/models/user.py:584 #: users/models/user.py:574
msgid "Administrator is the super user of system" msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员" msgstr "Administrator是初始的超级管理员"
...@@ -5057,7 +5054,7 @@ msgstr "安全令牌验证" ...@@ -5057,7 +5054,7 @@ msgstr "安全令牌验证"
#: users/templates/users/_base_otp.html:44 users/templates/users/_user.html:13 #: users/templates/users/_base_otp.html:44 users/templates/users/_user.html:13
#: users/templates/users/user_profile_update.html:55 #: users/templates/users/user_profile_update.html:55
#: xpack/plugins/cloud/models.py:147 #: xpack/plugins/cloud/models.py:147
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:60 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:57
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:13 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:13
msgid "Account" msgid "Account"
msgstr "账户" msgstr "账户"
...@@ -5347,7 +5344,7 @@ msgid "User group detail" ...@@ -5347,7 +5344,7 @@ msgid "User group detail"
msgstr "用户组详情" msgstr "用户组详情"
#: users/templates/users/user_group_detail.html:81 #: users/templates/users/user_group_detail.html:81
#: xpack/plugins/orgs/templates/orgs/org_detail.html:121 #: xpack/plugins/orgs/templates/orgs/org_detail.html:116
msgid "Add user" msgid "Add user"
msgstr "添加用户" msgstr "添加用户"
...@@ -5372,52 +5369,28 @@ msgstr "用户组删除" ...@@ -5372,52 +5369,28 @@ msgstr "用户组删除"
msgid "UserGroup Deleting failed." msgid "UserGroup Deleting failed."
msgstr "用户组删除失败" msgstr "用户组删除失败"
#: users/templates/users/user_list.html:53 #: users/templates/users/user_list.html:251
msgid "Remove selected"
msgstr "批量移除"
#: users/templates/users/user_list.html:129
#: users/templates/users/user_list.html:133
msgid "Remove"
msgstr "移除"
#: users/templates/users/user_list.html:271
msgid "This will delete the selected users !!!" msgid "This will delete the selected users !!!"
msgstr "删除选中用户 !!!" msgstr "删除选中用户 !!!"
#: users/templates/users/user_list.html:282 #: users/templates/users/user_list.html:262
msgid "User Deleting failed." msgid "User Deleted."
msgstr "用户删除失败" msgstr "已被删除"
#: users/templates/users/user_list.html:283 #: users/templates/users/user_list.html:263
#: users/templates/users/user_list.html:267
msgid "User Delete" msgid "User Delete"
msgstr "删除" msgstr "删除"
#: users/templates/users/user_list.html:305 #: users/templates/users/user_list.html:266
msgid "This will remove the selected users !!" msgid "User Deleting failed."
msgstr "移除选中用户 !!!" msgstr "用户删除失败"
#: users/templates/users/user_list.html:307
msgid "User Removing failed."
msgstr "用户移除失败"
#: users/templates/users/user_list.html:308
msgid "User Remove"
msgstr "用户移除"
#: users/templates/users/user_list.html:357
msgid "Are you sure about removing it?"
msgstr "您确定移除吗?"
#: users/templates/users/user_list.html:358
msgid "Remove the success"
msgstr "移除成功"
#: users/templates/users/user_list.html:363 #: users/templates/users/user_list.html:327
msgid "User is expired" msgid "User is expired"
msgstr "用户已失效" msgstr "用户已失效"
#: users/templates/users/user_list.html:366 #: users/templates/users/user_list.html:330
msgid "User is inactive" msgid "User is inactive"
msgstr "用户已禁用" msgstr "用户已禁用"
...@@ -5804,16 +5777,16 @@ msgid "Password length" ...@@ -5804,16 +5777,16 @@ msgid "Password length"
msgstr "密码长度" msgstr "密码长度"
#: xpack/plugins/change_auth_plan/forms.py:75 #: xpack/plugins/change_auth_plan/forms.py:75
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:58 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:56
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:81 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:79
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:17 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:17
#: xpack/plugins/cloud/forms.py:33 xpack/plugins/cloud/forms.py:87 #: xpack/plugins/cloud/forms.py:33 xpack/plugins/cloud/forms.py:87
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:41 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:37
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:72 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:69
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:16 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:16
#: xpack/plugins/gathered_user/forms.py:13 #: xpack/plugins/gathered_user/forms.py:13
#: xpack/plugins/gathered_user/forms.py:41 #: xpack/plugins/gathered_user/forms.py:41
#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:32 #: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:28
msgid "Periodic perform" msgid "Periodic perform"
msgstr "定时执行" msgstr "定时执行"
...@@ -5869,9 +5842,9 @@ msgstr "所有资产使用不同的随机密码" ...@@ -5869,9 +5842,9 @@ msgstr "所有资产使用不同的随机密码"
#: xpack/plugins/change_auth_plan/models.py:79 #: xpack/plugins/change_auth_plan/models.py:79
#: xpack/plugins/change_auth_plan/models.py:148 #: xpack/plugins/change_auth_plan/models.py:148
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:100 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:98
#: xpack/plugins/cloud/models.py:165 xpack/plugins/cloud/models.py:219 #: xpack/plugins/cloud/models.py:165 xpack/plugins/cloud/models.py:219
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:91 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:88
#: xpack/plugins/gathered_user/models.py:35 #: xpack/plugins/gathered_user/models.py:35
#: xpack/plugins/gathered_user/models.py:72 #: xpack/plugins/gathered_user/models.py:72
msgid "Cycle perform" msgid "Cycle perform"
...@@ -5879,16 +5852,16 @@ msgstr "周期执行" ...@@ -5879,16 +5852,16 @@ msgstr "周期执行"
#: xpack/plugins/change_auth_plan/models.py:84 #: xpack/plugins/change_auth_plan/models.py:84
#: xpack/plugins/change_auth_plan/models.py:146 #: xpack/plugins/change_auth_plan/models.py:146
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:92 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:90
#: xpack/plugins/cloud/models.py:170 xpack/plugins/cloud/models.py:217 #: xpack/plugins/cloud/models.py:170 xpack/plugins/cloud/models.py:217
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:83 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:80
#: xpack/plugins/gathered_user/models.py:40 #: xpack/plugins/gathered_user/models.py:40
#: xpack/plugins/gathered_user/models.py:70 #: xpack/plugins/gathered_user/models.py:70
msgid "Regularly perform" msgid "Regularly perform"
msgstr "定期执行" msgstr "定期执行"
#: xpack/plugins/change_auth_plan/models.py:93 #: xpack/plugins/change_auth_plan/models.py:93
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:74 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:72
msgid "Password rules" msgid "Password rules"
msgstr "密码规则" msgstr "密码规则"
...@@ -5942,46 +5915,46 @@ msgstr "* 密码长度范围 6-30 位" ...@@ -5942,46 +5915,46 @@ msgstr "* 密码长度范围 6-30 位"
msgid "* Please enter a valid crontab expression" msgid "* Please enter a valid crontab expression"
msgstr "* 请输入有效的 crontab 表达式" msgstr "* 请输入有效的 crontab 表达式"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:23 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:19
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:26 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:24
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:23 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:23
#: xpack/plugins/change_auth_plan/views.py:133 #: xpack/plugins/change_auth_plan/views.py:133
msgid "Plan execution list" msgid "Plan execution list"
msgstr "执行列表" msgstr "执行列表"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:66 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:62
msgid "Add asset to this plan" msgid "Add asset to this plan"
msgstr "添加资产" msgstr "添加资产"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:91 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:87
msgid "Add node to this plan" msgid "Add node to this plan"
msgstr "添加节点" msgstr "添加节点"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:11 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:8
msgid "" msgid ""
"When the user password on the asset is changed, the action is performed " "When the user password on the asset is changed, the action is performed "
"using the admin user associated with the asset" "using the admin user associated with the asset"
msgstr "更改资产上的用户密码时,将会使用与该资产关联的管理用户进行操作" msgstr "更改资产上的用户密码时,将会使用与该资产关联的管理用户进行操作"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:76 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:74
msgid "Length" msgid "Length"
msgstr "长度" msgstr "长度"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:84 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:82
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:75 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:72
msgid "Yes" msgid "Yes"
msgstr "是" msgstr "是"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:86 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:84
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:77 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:74
msgid "No" msgid "No"
msgstr "否" msgstr "否"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:134 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:132
msgid "Run plan manually" msgid "Run plan manually"
msgstr "手动执行计划" msgstr "手动执行计划"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:178 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:176
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:102 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:102
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:90 #: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:90
msgid "Execute failed" msgid "Execute failed"
...@@ -5992,7 +5965,7 @@ msgid "Execution list of plan" ...@@ -5992,7 +5965,7 @@ msgid "Execution list of plan"
msgstr "执行列表" msgstr "执行列表"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:104 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:104
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:89 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:84
msgid "Log" msgid "Log"
msgstr "日志" msgstr "日志"
...@@ -6071,7 +6044,7 @@ msgid "Unavailable" ...@@ -6071,7 +6044,7 @@ msgid "Unavailable"
msgstr "无效" msgstr "无效"
#: xpack/plugins/cloud/models.py:63 #: xpack/plugins/cloud/models.py:63
#: xpack/plugins/cloud/templates/cloud/account_detail.html:54 #: xpack/plugins/cloud/templates/cloud/account_detail.html:51
#: xpack/plugins/cloud/templates/cloud/account_list.html:13 #: xpack/plugins/cloud/templates/cloud/account_list.html:13
msgid "Provider" msgid "Provider"
msgstr "云服务商" msgstr "云服务商"
...@@ -6085,7 +6058,7 @@ msgid "Access key secret" ...@@ -6085,7 +6058,7 @@ msgid "Access key secret"
msgstr "" msgstr ""
#: xpack/plugins/cloud/models.py:88 #: xpack/plugins/cloud/models.py:88
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:30 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:26
msgid "Cloud account" msgid "Cloud account"
msgstr "云账号" msgstr "云账号"
...@@ -6098,7 +6071,7 @@ msgid "Instances" ...@@ -6098,7 +6071,7 @@ msgid "Instances"
msgstr "实例" msgstr "实例"
#: xpack/plugins/cloud/models.py:176 #: xpack/plugins/cloud/models.py:176
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:97 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:94
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:17 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:17
msgid "Date last sync" msgid "Date last sync"
msgstr "最后同步日期" msgstr "最后同步日期"
...@@ -6116,8 +6089,8 @@ msgid "Partial succeed" ...@@ -6116,8 +6089,8 @@ msgid "Partial succeed"
msgstr "" msgstr ""
#: xpack/plugins/cloud/models.py:281 xpack/plugins/cloud/models.py:313 #: xpack/plugins/cloud/models.py:281 xpack/plugins/cloud/models.py:313
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:71 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:66
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:66 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:63
msgid "Date sync" msgid "Date sync"
msgstr "同步日期" msgstr "同步日期"
...@@ -6134,8 +6107,8 @@ msgid "Sync instance task history" ...@@ -6134,8 +6107,8 @@ msgid "Sync instance task history"
msgstr "同步实例任务历史" msgstr "同步实例任务历史"
#: xpack/plugins/cloud/models.py:301 #: xpack/plugins/cloud/models.py:301
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:117 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:114
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:61 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:58
msgid "Instance" msgid "Instance"
msgstr "实例" msgstr "实例"
...@@ -6155,7 +6128,7 @@ msgstr "AWS (国际)" ...@@ -6155,7 +6128,7 @@ msgstr "AWS (国际)"
msgid "Qcloud" msgid "Qcloud"
msgstr "腾讯云" msgstr "腾讯云"
#: xpack/plugins/cloud/templates/cloud/account_detail.html:20 #: xpack/plugins/cloud/templates/cloud/account_detail.html:17
#: xpack/plugins/cloud/views.py:79 #: xpack/plugins/cloud/views.py:79
msgid "Account detail" msgid "Account detail"
msgstr "账户详情" msgstr "账户详情"
...@@ -6165,61 +6138,61 @@ msgstr "账户详情" ...@@ -6165,61 +6138,61 @@ msgstr "账户详情"
msgid "Create account" msgid "Create account"
msgstr "创建账户" msgstr "创建账户"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:33 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:29
msgid "Region & Instance" msgid "Region & Instance"
msgstr "地域 & 实例" msgstr "地域 & 实例"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:37 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:33
msgid "Node & AdminUser" msgid "Node & AdminUser"
msgstr "节点 & 管理用户" msgstr "节点 & 管理用户"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:67 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:63
msgid "Load failed" msgid "Load failed"
msgstr "加载失败" msgstr "加载失败"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:20 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:17
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:25 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:20
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:21 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:18
#: xpack/plugins/cloud/views.py:144 #: xpack/plugins/cloud/views.py:144
msgid "Sync task detail" msgid "Sync task detail"
msgstr "同步任务详情" msgstr "同步任务详情"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:23 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:20
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:28 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:23
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:24 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:21
#: xpack/plugins/cloud/views.py:160 #: xpack/plugins/cloud/views.py:160
msgid "Sync task history" msgid "Sync task history"
msgstr "同步历史列表" msgstr "同步历史列表"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:26 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:23
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:31 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:26
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:27 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:24
#: xpack/plugins/cloud/views.py:212 #: xpack/plugins/cloud/views.py:212
msgid "Sync instance list" msgid "Sync instance list"
msgstr "同步实例列表" msgstr "同步实例列表"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:138 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:135
msgid "Run task manually" msgid "Run task manually"
msgstr "手动执行任务" msgstr "手动执行任务"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:181 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:178
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:99 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:99
msgid "Sync success" msgid "Sync success"
msgstr "同步成功" msgstr "同步成功"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:65 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:60
msgid "Total count" msgid "Total count"
msgstr "总数" msgstr "总数"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:66 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:61
msgid "Succeed count" msgid "Succeed count"
msgstr "成功" msgstr "成功"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:67 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:62
msgid "Failed count" msgid "Failed count"
msgstr "失败" msgstr "失败"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:68 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:63
msgid "Exist count" msgid "Exist count"
msgstr "存在" msgstr "存在"
...@@ -6267,19 +6240,19 @@ msgid "Periodic" ...@@ -6267,19 +6240,19 @@ msgid "Periodic"
msgstr "定时执行" msgstr "定时执行"
#: xpack/plugins/gathered_user/models.py:57 #: xpack/plugins/gathered_user/models.py:57
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:38 #: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:43
msgid "Gather user task" msgid "Gather user task"
msgstr "收集用户任务" msgstr "收集用户任务"
#: xpack/plugins/gathered_user/models.py:137 #: xpack/plugins/gathered_user/models.py:140
msgid "Task" msgid "Task"
msgstr "任务" msgstr "任务"
#: xpack/plugins/gathered_user/models.py:149 #: xpack/plugins/gathered_user/models.py:152
msgid "gather user task execution" msgid "gather user task execution"
msgstr "收集用户执行" msgstr "收集用户执行"
#: xpack/plugins/gathered_user/models.py:155 #: xpack/plugins/gathered_user/models.py:158
msgid "Assets is empty, please change nodes" msgid "Assets is empty, please change nodes"
msgstr "资产为空,请更改节点" msgstr "资产为空,请更改节点"
...@@ -6377,8 +6350,8 @@ msgid "It is already in the default setting state!" ...@@ -6377,8 +6350,8 @@ msgid "It is already in the default setting state!"
msgstr "当前已经是初始化状态!" msgstr "当前已经是初始化状态!"
#: xpack/plugins/license/meta.py:11 xpack/plugins/license/models.py:94 #: xpack/plugins/license/meta.py:11 xpack/plugins/license/models.py:94
#: xpack/plugins/license/templates/license/license_detail.html:41 #: xpack/plugins/license/templates/license/license_detail.html:50
#: xpack/plugins/license/templates/license/license_detail.html:46 #: xpack/plugins/license/templates/license/license_detail.html:55
#: xpack/plugins/license/views.py:32 #: xpack/plugins/license/views.py:32
msgid "License" msgid "License"
msgstr "许可证" msgstr "许可证"
...@@ -6392,7 +6365,7 @@ msgid "Enterprise edition" ...@@ -6392,7 +6365,7 @@ msgid "Enterprise edition"
msgstr "企业版" msgstr "企业版"
#: xpack/plugins/license/templates/license/_license_import_modal.html:4 #: xpack/plugins/license/templates/license/_license_import_modal.html:4
#: xpack/plugins/license/templates/license/license_detail.html:99 #: xpack/plugins/license/templates/license/license_detail.html:108
msgid "Import license" msgid "Import license"
msgstr "导入许可证" msgstr "导入许可证"
...@@ -6400,64 +6373,64 @@ msgstr "导入许可证" ...@@ -6400,64 +6373,64 @@ msgstr "导入许可证"
msgid "License file" msgid "License file"
msgstr "许可证文件" msgstr "许可证文件"
#: xpack/plugins/license/templates/license/license_detail.html:11 #: xpack/plugins/license/templates/license/license_detail.html:12
msgid "Please Import License" msgid "Please Import License"
msgstr "请导入许可证" msgstr "请导入许可证"
#: xpack/plugins/license/templates/license/license_detail.html:13 #: xpack/plugins/license/templates/license/license_detail.html:17
#: xpack/plugins/license/templates/license/license_detail.html:47 #: xpack/plugins/license/templates/license/license_detail.html:56
msgid "License has expired" msgid "License has expired"
msgstr "许可证已经过期" msgstr "许可证已经过期"
#: xpack/plugins/license/templates/license/license_detail.html:15 #: xpack/plugins/license/templates/license/license_detail.html:22
msgid "The license will at " msgid "The license will at "
msgstr "许可证将在 " msgstr "许可证将在 "
#: xpack/plugins/license/templates/license/license_detail.html:15 #: xpack/plugins/license/templates/license/license_detail.html:22
msgid " expired." msgid " expired."
msgstr " 过期。" msgstr " 过期。"
#: xpack/plugins/license/templates/license/license_detail.html:28 #: xpack/plugins/license/templates/license/license_detail.html:37
#: xpack/plugins/license/views.py:33 #: xpack/plugins/license/views.py:33
msgid "License detail" msgid "License detail"
msgstr "许可证详情" msgstr "许可证详情"
#: xpack/plugins/license/templates/license/license_detail.html:42 #: xpack/plugins/license/templates/license/license_detail.html:51
msgid "No license" msgid "No license"
msgstr "暂无许可证" msgstr "暂无许可证"
#: xpack/plugins/license/templates/license/license_detail.html:51 #: xpack/plugins/license/templates/license/license_detail.html:60
msgid "Subscription ID" msgid "Subscription ID"
msgstr "订阅授权ID" msgstr "订阅授权ID"
#: xpack/plugins/license/templates/license/license_detail.html:55 #: xpack/plugins/license/templates/license/license_detail.html:64
msgid "Corporation" msgid "Corporation"
msgstr "公司" msgstr "公司"
#: xpack/plugins/license/templates/license/license_detail.html:59 #: xpack/plugins/license/templates/license/license_detail.html:68
msgid "Expired" msgid "Expired"
msgstr "过期时间" msgstr "过期时间"
#: xpack/plugins/license/templates/license/license_detail.html:64 #: xpack/plugins/license/templates/license/license_detail.html:73
#: xpack/plugins/license/templates/license/license_detail.html:68 #: xpack/plugins/license/templates/license/license_detail.html:77
#: xpack/plugins/license/templates/license/license_detail.html:72 #: xpack/plugins/license/templates/license/license_detail.html:81
#: xpack/plugins/license/templates/license/license_detail.html:76 #: xpack/plugins/license/templates/license/license_detail.html:85
msgid "Unlimited" msgid "Unlimited"
msgstr "无限制" msgstr "无限制"
#: xpack/plugins/license/templates/license/license_detail.html:75 #: xpack/plugins/license/templates/license/license_detail.html:84
msgid "Concurrent connections" msgid "Concurrent connections"
msgstr "并发连接" msgstr "并发连接"
#: xpack/plugins/license/templates/license/license_detail.html:80 #: xpack/plugins/license/templates/license/license_detail.html:89
msgid "Edition" msgid "Edition"
msgstr "版本" msgstr "版本"
#: xpack/plugins/license/templates/license/license_detail.html:106 #: xpack/plugins/license/templates/license/license_detail.html:115
msgid "Technology consulting" msgid "Technology consulting"
msgstr "技术咨询" msgstr "技术咨询"
#: xpack/plugins/license/templates/license/license_detail.html:109 #: xpack/plugins/license/templates/license/license_detail.html:118
msgid "Consult" msgid "Consult"
msgstr "咨询" msgstr "咨询"
...@@ -6474,7 +6447,7 @@ msgid "Select auditor" ...@@ -6474,7 +6447,7 @@ msgid "Select auditor"
msgstr "选择审计员" msgstr "选择审计员"
#: xpack/plugins/orgs/forms.py:29 #: xpack/plugins/orgs/forms.py:29
#: xpack/plugins/orgs/templates/orgs/org_detail.html:76 #: xpack/plugins/orgs/templates/orgs/org_detail.html:71
#: xpack/plugins/orgs/templates/orgs/org_list.html:13 #: xpack/plugins/orgs/templates/orgs/org_list.html:13
msgid "Admin" msgid "Admin"
msgstr "管理员" msgstr "管理员"
...@@ -6485,12 +6458,12 @@ msgstr "管理员" ...@@ -6485,12 +6458,12 @@ msgstr "管理员"
msgid "Organizations" msgid "Organizations"
msgstr "组织管理" msgstr "组织管理"
#: xpack/plugins/orgs/templates/orgs/org_detail.html:22 #: xpack/plugins/orgs/templates/orgs/org_detail.html:17
#: xpack/plugins/orgs/views.py:80 #: xpack/plugins/orgs/views.py:80
msgid "Org detail" msgid "Org detail"
msgstr "组织详情" msgstr "组织详情"
#: xpack/plugins/orgs/templates/orgs/org_detail.html:84 #: xpack/plugins/orgs/templates/orgs/org_detail.html:79
msgid "Add admin" msgid "Add admin"
msgstr "添加管理员" msgstr "添加管理员"
...@@ -6527,8 +6500,15 @@ msgstr "密码匣子" ...@@ -6527,8 +6500,15 @@ msgstr "密码匣子"
msgid "vault create" msgid "vault create"
msgstr "创建" msgstr "创建"
#~ msgid "User Deleted." #, fuzzy
#~ msgstr "已被删除" #~| msgid "Platform list"
#~ msgid "Platform create"
#~ msgstr "平台列表"
#, fuzzy
#~| msgid "Password update"
#~ msgid "Platform update"
#~ msgstr "密码更新"
#~ msgid "Search no entry matched in ou {}" #~ msgid "Search no entry matched in ou {}"
#~ msgstr "在ou:{}中没有匹配条目" #~ msgstr "在ou:{}中没有匹配条目"
...@@ -6583,6 +6563,9 @@ msgstr "创建" ...@@ -6583,6 +6563,9 @@ msgstr "创建"
#~ msgid "Delete failed" #~ msgid "Delete failed"
#~ msgstr "删除失败" #~ msgstr "删除失败"
#~ msgid "Are you sure about deleting it?"
#~ msgstr "您确定删除吗?"
#~ msgid "The connection fails" #~ msgid "The connection fails"
#~ msgstr "连接失败" #~ msgstr "连接失败"
...@@ -6612,6 +6595,9 @@ msgstr "创建" ...@@ -6612,6 +6595,9 @@ msgstr "创建"
#~ msgid "Approve selected" #~ msgid "Approve selected"
#~ msgstr "同意所选" #~ msgstr "同意所选"
#~ msgid "Reject selected"
#~ msgstr "拒绝所选"
#~ msgid "" #~ msgid ""
#~ "\n" #~ "\n"
#~ " <div>\n" #~ " <div>\n"
......
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
{% block content %} {% block content %}
<div class="wrapper wrapper-content"> <div class="wrapper wrapper-content">
<div class="row"> <div class="row">
<div class="col-lg-3" id="split-left" style="padding-left: 3px"> <div class="col-sm-3" id="split-left" style="padding-left: 3px">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="ibox-content mailbox-content" <div class="ibox-content mailbox-content"
style="padding-top: 0;padding-left: 1px"> style="padding-top: 0;padding-left: 1px">
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-9 animated fadeInRight" id="split-right"> <div class="col-sm-9 animated fadeInRight" id="split-right">
<div class="tree-toggle"> <div class="tree-toggle">
<div class="btn btn-sm btn-primary tree-toggle-btn" <div class="btn btn-sm btn-primary tree-toggle-btn"
onclick="toggle()"> onclick="toggle()">
...@@ -87,14 +87,14 @@ ...@@ -87,14 +87,14 @@
style="height: 100%;width: 100%"></div> style="height: 100%;width: 100%"></div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-10"> <div class="col-sm-10">
<div class="input-group" <div class="input-group"
style="height: 100%; width: 100%"> style="height: 100%; width: 100%">
<textarea class="form-control" <textarea class="form-control"
id="command-text"></textarea> id="command-text"></textarea>
</div> </div>
</div> </div>
<div class="col-lg-2"> <div class="col-sm-2">
<select class="select2 form-control" <select class="select2 form-control"
id="system-users-select"> id="system-users-select">
{% for s in system_users %} {% for s in system_users %}
...@@ -199,12 +199,12 @@ ...@@ -199,12 +199,12 @@
function toggle() { function toggle() {
if (show === 0) { if (show === 0) {
$("#split-left").hide(500, function () { $("#split-left").hide(500, function () {
$("#split-right").attr("class", "col-lg-12"); $("#split-right").attr("class", "col-sm-12");
$("#toggle-icon").attr("class", "fa fa-angle-right fa-x"); $("#toggle-icon").attr("class", "fa fa-angle-right fa-x");
show = 1; show = 1;
}); });
} else { } else {
$("#split-right").attr("class", "col-lg-9"); $("#split-right").attr("class", "col-sm-9");
$("#toggle-icon").attr("class", "fa fa-angle-left fa-x"); $("#toggle-icon").attr("class", "fa fa-angle-left fa-x");
$("#split-left").show(500); $("#split-left").show(500);
show = 0; show = 0;
......
...@@ -23,8 +23,10 @@ class AssetPermissionViewSet(OrgModelViewSet): ...@@ -23,8 +23,10 @@ class AssetPermissionViewSet(OrgModelViewSet):
资产授权列表的增删改查api 资产授权列表的增删改查api
""" """
model = AssetPermission model = AssetPermission
serializer_class = serializers.AssetPermissionCreateUpdateSerializer serializer_classes = {
serializer_display_class = serializers.AssetPermissionListSerializer 'default': serializers.AssetPermissionCreateUpdateSerializer,
'display': serializers.AssetPermissionListSerializer
}
filter_fields = ['name'] filter_fields = ['name']
permission_classes = (IsOrgAdmin,) permission_classes = (IsOrgAdmin,)
......
{% extends 'base.html' %} {% extends '_base_asset_tree_list.html' %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
...@@ -12,56 +12,36 @@ ...@@ -12,56 +12,36 @@
.toggle { .toggle {
cursor: pointer; cursor: pointer;
} }
.detail-key {
width: 70px;
}
</style> </style>
{% endblock %} {% endblock %}
{% block content %} {% block table_container %}
<div class="wrapper wrapper-content"> <div class="btn-group uc pull-left m-r-5">
<div class="row"> <button class="btn btn-sm btn-primary btn-create-permission">
<div class="col-lg-3" id="split-left" style="padding-left: 3px;padding-right: 0"> {% trans "Create permission" %}
{% include 'assets/_node_tree.html' %} </button>
</div> <button data-toggle="dropdown" class="btn btn-primary btn-sm dropdown-toggle"><span class="caret"></span></button>
<div class="col-lg-9 animated fadeInRight" id="split-right"> <ul class="dropdown-menu">
<div class="tree-toggle"> <li><a class="refresh-asset-permission-cache" href="#">{% trans 'Refresh permission cache' %}</a></li>
<div class="btn btn-sm btn-primary tree-toggle-btn"> </ul>
<i class="fa fa-angle-left fa-x" id="toggle-icon"></i> </div>
</div> <table class="table table-striped table-bordered table-hover" id="permission_list_table" style="width: 100%">
</div> <thead>
<div class="mail-box-header"> <tr>
<div class="btn-group uc pull-left m-r-5"> <th></th>
<button class="btn btn-sm btn-primary btn-create-permission"> <th>{% trans 'Name' %}</th>
{% trans "Create permission" %} <th class="text-center">{% trans 'User' %}</th>
</button> <th class="text-center">{% trans 'User group' %}</th>
<button data-toggle="dropdown" class="btn btn-primary btn-sm dropdown-toggle"><span class="caret"></span></button> <th class="text-center">{% trans 'Asset' %}</th>
<ul class="dropdown-menu"> <th class="text-center">{% trans 'Node'%}</th>
<li><a class="refresh-asset-permission-cache" href="#">{% trans 'Refresh permission cache' %}</a></li> <th class="text-center">{% trans 'System user' %}</th>
</ul> <th class="text-center">{% trans 'Validity' %}</th>
</div> <th class="text-center" >{% trans 'Action' %}</th>
<table class="table table-striped table-bordered table-hover" id="permission_list_table" style="width: 100%"> </tr>
<thead> </thead>
<tr> <tbody>
<th></th> </tbody>
<th>{% trans 'Name' %}</th> </table>
<th class="text-center">{% trans 'User' %}</th>
<th class="text-center">{% trans 'User group' %}</th>
<th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Node'%}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Validity' %}</th>
<th class="text-center" >{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
{% include '_filter_dropdown.html' %} {% include '_filter_dropdown.html' %}
{% endblock %} {% endblock %}
......
...@@ -13,71 +13,74 @@ router.register('asset-permissions-nodes-relations', api.AssetPermissionNodeRela ...@@ -13,71 +13,74 @@ router.register('asset-permissions-nodes-relations', api.AssetPermissionNodeRela
router.register('asset-permissions-system-users-relations', api.AssetPermissionSystemUserRelationViewSet, 'asset-permissions-system-users-relation') router.register('asset-permissions-system-users-relations', api.AssetPermissionSystemUserRelationViewSet, 'asset-permissions-system-users-relation')
user_permission_urlpatterns = [ user_permission_urlpatterns = [
path('users/<uuid:pk>/assets/', api.UserGrantedAssetsApi.as_view(), name='user-assets'), path('<uuid:pk>/assets/', api.UserGrantedAssetsApi.as_view(), name='user-assets'),
path('users/assets/', api.UserGrantedAssetsApi.as_view(), name='my-assets'), path('assets/', api.UserGrantedAssetsApi.as_view(), name='my-assets'),
# Assets as tree # Assets as tree
path('users/<uuid:pk>/assets/tree/', api.UserGrantedAssetsAsTreeApi.as_view(), name='user-assets-as-tree'), path('<uuid:pk>/assets/tree/', api.UserGrantedAssetsAsTreeApi.as_view(), name='user-assets-as-tree'),
path('users/assets/tree/', api.UserGrantedAssetsAsTreeApi.as_view(), name='my-assets-as-tree'), path('assets/tree/', api.UserGrantedAssetsAsTreeApi.as_view(), name='my-assets-as-tree'),
# Nodes # Nodes
path('users/<uuid:pk>/nodes/', api.UserGrantedNodesApi.as_view(), name='user-nodes'), path('<uuid:pk>/nodes/', api.UserGrantedNodesApi.as_view(), name='user-nodes'),
path('users/nodes/', api.UserGrantedNodesApi.as_view(), name='my-nodes'), path('nodes/', api.UserGrantedNodesApi.as_view(), name='my-nodes'),
# Node children # Node children
path('users/<uuid:pk>/nodes/children/', api.UserGrantedNodesApi.as_view(), name='user-nodes-children'), path('<uuid:pk>/nodes/children/', api.UserGrantedNodesApi.as_view(), name='user-nodes-children'),
path('users/nodes/children/', api.UserGrantedNodesApi.as_view(), name='my-nodes-children'), path('nodes/children/', api.UserGrantedNodesApi.as_view(), name='my-nodes-children'),
# Node as tree # Node as tree
path('users/<uuid:pk>/nodes/tree/', api.UserGrantedNodesAsTreeApi.as_view(), name='user-nodes-as-tree'), path('<uuid:pk>/nodes/tree/', api.UserGrantedNodesAsTreeApi.as_view(), name='user-nodes-as-tree'),
path('users/nodes/tree/', api.UserGrantedNodesAsTreeApi.as_view(), name='my-nodes-as-tree'), path('nodes/tree/', api.UserGrantedNodesAsTreeApi.as_view(), name='my-nodes-as-tree'),
# Node with assets as tree # Node with assets as tree
path('users/<uuid:pk>/nodes-with-assets/tree/', api.UserGrantedNodesWithAssetsAsTreeApi.as_view(), name='user-nodes-with-assets-as-tree'), path('<uuid:pk>/nodes-with-assets/tree/', api.UserGrantedNodesWithAssetsAsTreeApi.as_view(), name='user-nodes-with-assets-as-tree'),
path('users/nodes-with-assets/tree/', api.UserGrantedNodesWithAssetsAsTreeApi.as_view(), name='my-nodes-with-assets-as-tree'), path('nodes-with-assets/tree/', api.UserGrantedNodesWithAssetsAsTreeApi.as_view(), name='my-nodes-with-assets-as-tree'),
# Node children as tree # Node children as tree
path('users/<uuid:pk>/nodes/children/tree/', api.UserGrantedNodeChildrenAsTreeApi.as_view(), name='user-nodes-children-as-tree'), path('<uuid:pk>/nodes/children/tree/', api.UserGrantedNodeChildrenAsTreeApi.as_view(), name='user-nodes-children-as-tree'),
path('users/nodes/children/tree/', api.UserGrantedNodeChildrenAsTreeApi.as_view(), name='my-nodes-children-as-tree'), path('nodes/children/tree/', api.UserGrantedNodeChildrenAsTreeApi.as_view(), name='my-nodes-children-as-tree'),
# Node children with assets as tree # Node children with assets as tree
path('users/<uuid:pk>/nodes/children-with-assets/tree/', api.UserGrantedNodeChildrenWithAssetsAsTreeApi.as_view(), name='user-nodes-children-with-assets-as-tree'), path('<uuid:pk>/nodes/children-with-assets/tree/', api.UserGrantedNodeChildrenWithAssetsAsTreeApi.as_view(), name='user-nodes-children-with-assets-as-tree'),
path('users/nodes/children-with-assets/tree/', api.UserGrantedNodeChildrenWithAssetsAsTreeApi.as_view(), name='my-nodes-children-with-assets-as-tree'), path('nodes/children-with-assets/tree/', api.UserGrantedNodeChildrenWithAssetsAsTreeApi.as_view(), name='my-nodes-children-with-assets-as-tree'),
# Node assets # Node assets
path('users/<uuid:pk>/nodes/<uuid:node_id>/assets/', api.UserGrantedNodeAssetsApi.as_view(), name='user-node-assets'), path('<uuid:pk>/nodes/<uuid:node_id>/assets/', api.UserGrantedNodeAssetsApi.as_view(), name='user-node-assets'),
path('users/nodes/<uuid:node_id>/assets/', api.UserGrantedNodeAssetsApi.as_view(), name='my-node-assets'), path('nodes/<uuid:node_id>/assets/', api.UserGrantedNodeAssetsApi.as_view(), name='my-node-assets'),
# Asset System users # Asset System users
path('users/<uuid:pk>/assets/<uuid:asset_id>/system-users/', api.UserGrantedAssetSystemUsersApi.as_view(), name='user-asset-system-users'), path('users/<uuid:pk>/assets/<uuid:asset_id>/system-users/', api.UserGrantedAssetSystemUsersApi.as_view(), name='user-asset-system-users'),
path('users/assets/<uuid:asset_id>/system-users/', api.UserGrantedAssetSystemUsersApi.as_view(), name='my-asset-system-users'), path('assets/<uuid:asset_id>/system-users/', api.UserGrantedAssetSystemUsersApi.as_view(), name='my-asset-system-users'),
] ]
user_group_permission_urlpatterns = [ user_group_permission_urlpatterns = [
# 查询某个用户组授权的资产和资产组 # 查询某个用户组授权的资产和资产组
path('user-groups/<uuid:pk>/assets/', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'), path('<uuid:pk>/assets/', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'),
path('user-groups/<uuid:pk>/nodes/', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'), path('<uuid:pk>/nodes/', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'),
path('user-groups/<uuid:pk>/nodes/children/', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes-children'), path('<uuid:pk>/nodes/children/', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes-children'),
path('user-groups/<uuid:pk>/nodes/children/tree/', api.UserGroupGrantedNodeChildrenAsTreeApi.as_view(), name='user-group-nodes-children-as-tree'), path('<uuid:pk>/nodes/children/tree/', api.UserGroupGrantedNodeChildrenAsTreeApi.as_view(), name='user-group-nodes-children-as-tree'),
path('user-groups/<uuid:pk>/nodes/<uuid:node_id>/assets/', api.UserGroupGrantedNodeAssetsApi.as_view(), name='user-group-node-assets'), path('<uuid:pk>/nodes/<uuid:node_id>/assets/', api.UserGroupGrantedNodeAssetsApi.as_view(), name='user-group-node-assets'),
path('user-groups/<uuid:pk>/assets/<uuid:asset_id>/system-users/', api.UserGroupGrantedAssetSystemUsersApi.as_view(), name='user-group-asset-system-users'), path('<uuid:pk>/assets/<uuid:asset_id>/system-users/', api.UserGroupGrantedAssetSystemUsersApi.as_view(), name='user-group-asset-system-users'),
] ]
asset_permission_urlpatterns = [ permission_urlpatterns = [
# Assets
path('', include(user_permission_urlpatterns)),
# 授权规则中授权的资产 # 授权规则中授权的资产
path('asset-permissions/<uuid:pk>/assets/all/', api.AssetPermissionAllAssetListApi.as_view(), name='asset-permission-all-assets'), path('<uuid:pk>/assets/all/', api.AssetPermissionAllAssetListApi.as_view(), name='asset-permission-all-assets'),
path('asset-permissions/<uuid:pk>/users/all/', api.AssetPermissionAllUserListApi.as_view(), name='asset-permission-all-users'), path('<uuid:pk>/users/all/', api.AssetPermissionAllUserListApi.as_view(), name='asset-permission-all-users'),
# 验证用户是否有某个资产和系统用户的权限 # 验证用户是否有某个资产和系统用户的权限
path('asset-permissions/user/validate/', api.ValidateUserAssetPermissionApi.as_view(), name='validate-user-asset-permission'), path('user/validate/', api.ValidateUserAssetPermissionApi.as_view(), name='validate-user-asset-permission'),
path('asset-permissions/user/actions/', api.GetUserAssetPermissionActionsApi.as_view(), name='get-user-asset-permission-actions'), path('user/actions/', api.GetUserAssetPermissionActionsApi.as_view(), name='get-user-asset-permission-actions'),
# 刷新缓存 # 刷新缓存
path('asset-permissions/cache/refresh/', api.RefreshAssetPermissionCacheApi.as_view(), name='refresh-asset-permission-cache'), path('cache/refresh/', api.RefreshAssetPermissionCacheApi.as_view(), name='refresh-asset-permission-cache'),
]
asset_permission_urlpatterns = [
# Assets
path('users/', include(user_permission_urlpatterns)),
path('user-groups/', include(user_group_permission_urlpatterns)),
path('asset-permissions/', include(permission_urlpatterns)),
] ]
asset_permission_urlpatterns += router.urls asset_permission_urlpatterns += router.urls
{% load i18n %}
<ul class="nav nav-tabs">
<li id="tab-basic">
<a href="{% url 'settings:basic-setting' %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a>
</li>
<li id="tab-email" >
<a href="{% url 'settings:email-setting' %}" class="text-center"><i class="fa fa-envelope"></i> {% trans 'Email setting' %} </a>
</li>
<li id="tab-email-content" >
<a href="{% url 'settings:email-content-setting' %}" class="text-center"><i class="fa fa-file-text"></i> {% trans 'Email content setting' %} </a>
</li>
<li id="tab-ldap">
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li>
<li id="tab-terminal">
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
</li>
<li id="tab-security">
<a href="{% url 'settings:security-setting' %}" class="text-center"><i class="fa fa-lock"></i> {% trans 'Security setting' %} </a>
</li>
</ul>
<script>
$(document).ready(function () {
var path = location.pathname;
if (path.endsWith('/')) {
path = path.substring(0, path.length-1)
}
var pathList = path.split('/');
var tabId = pathList[pathList.length-1];
if (tabId === "settings") {
tabId = "basic"
}
tabId = "#tab-" + tabId;
$(tabId).addClass("active")
})
</script>
...@@ -10,26 +10,7 @@ ...@@ -10,26 +10,7 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="panel-options"> <div class="panel-options">
<ul class="nav nav-tabs"> {% include 'settings/_setting_tabs.html' %}
<li class="active">
<a href="" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a>
</li>
<li>
<a href="{% url 'settings:email-setting' %}" class="text-center"><i class="fa fa-envelope"></i> {% trans 'Email setting' %} </a>
</li>
<li>
<a href="{% url 'settings:email-content-setting' %}" class="text-center"><i class="fa fa-file-text"></i> {% trans 'Email content setting' %} </a>
</li>
<li>
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li>
<li>
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
</li>
<li>
<a href="{% url 'settings:security-setting' %}" class="text-center"><i class="fa fa-lock"></i> {% trans 'Security setting' %} </a>
</li>
</ul>
</div> </div>
<div class="tab-content"> <div class="tab-content">
<div class="col-sm-12" style="padding-left:0"> <div class="col-sm-12" style="padding-left:0">
......
...@@ -10,53 +10,33 @@ ...@@ -10,53 +10,33 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="panel-options"> <div class="panel-options">
<ul class="nav nav-tabs"> {% include 'settings/_setting_tabs.html' %}
<li>
<a href="{% url 'settings:basic-setting' %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a>
</li>
<li>
<a href="{% url 'settings:email-setting' %}" class="text-center"><i class="fa fa-envelope"></i> {% trans 'Email setting' %} </a>
</li>
<li class="active">
<a href="{% url 'settings:email-content-setting' %}" class="text-center"><i class="fa fa-file-text"></i> {% trans 'Email content setting' %} </a>
</li>
<li>
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li>
<li>
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
</li>
<li>
<a href="{% url 'settings:security-setting' %}" class="text-center"><i class="fa fa-lock"></i> {% trans 'Security setting' %} </a>
</li>
</ul>
</div> </div>
<div class="tab-content"> <div class="tab-content">
<div class="col-sm-12" style="padding-left:0"> <div class="col-sm-12" style="padding-left:0">
<div class="ibox-content" style="border-width: 0;padding-top: 40px;"> <div class="ibox-content" style="border-width: 0;padding-top: 40px;">
<form action="" method="post" class="form-horizontal"> <form action="" method="post" class="form-horizontal">
{% if form.non_field_errors %} {% if form.non_field_errors %}
<div class="alert alert-danger"> <div class="alert alert-danger">
{{ form.non_field_errors }} {{ form.non_field_errors }}
</div> </div>
{% endif %} {% endif %}
{% csrf_token %} {% csrf_token %}
<h3>{% trans "Create User setting" %}</h3> <h3>{% trans "Create User setting" %}</h3>
{% bootstrap_field form.EMAIL_CUSTOM_USER_CREATED_SUBJECT layout="horizontal" %} {% bootstrap_field form.EMAIL_CUSTOM_USER_CREATED_SUBJECT layout="horizontal" %}
{% bootstrap_field form.EMAIL_CUSTOM_USER_CREATED_HONORIFIC layout="horizontal" %} {% bootstrap_field form.EMAIL_CUSTOM_USER_CREATED_HONORIFIC layout="horizontal" %}
{% bootstrap_field form.EMAIL_CUSTOM_USER_CREATED_BODY layout="horizontal" %} {% bootstrap_field form.EMAIL_CUSTOM_USER_CREATED_BODY layout="horizontal" %}
{% bootstrap_field form.EMAIL_CUSTOM_USER_CREATED_SIGNATURE layout="horizontal" %} {% bootstrap_field form.EMAIL_CUSTOM_USER_CREATED_SIGNATURE layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-4 col-sm-offset-2"> <div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button> <button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button> <button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div> </div>
</form>
</div> </div>
</form>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -10,26 +10,7 @@ ...@@ -10,26 +10,7 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="panel-options"> <div class="panel-options">
<ul class="nav nav-tabs"> {% include 'settings/_setting_tabs.html' %}
<li>
<a href="{% url 'settings:basic-setting' %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a>
</li>
<li class="active">
<a href="{% url 'settings:email-setting' %}" class="text-center"><i class="fa fa-envelope"></i> {% trans 'Email setting' %} </a>
</li>
<li>
<a href="{% url 'settings:email-content-setting' %}" class="text-center"><i class="fa fa-file-text"></i> {% trans 'Email content setting' %} </a>
</li>
<li>
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li>
<li>
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
</li>
<li>
<a href="{% url 'settings:security-setting' %}" class="text-center"><i class="fa fa-lock"></i> {% trans 'Security setting' %} </a>
</li>
</ul>
</div> </div>
<div class="tab-content"> <div class="tab-content">
<div class="col-sm-12" style="padding-left:0"> <div class="col-sm-12" style="padding-left:0">
......
...@@ -10,26 +10,7 @@ ...@@ -10,26 +10,7 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="panel-options"> <div class="panel-options">
<ul class="nav nav-tabs"> {% include 'settings/_setting_tabs.html' %}
<li>
<a href="{% url 'settings:basic-setting' %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a>
</li>
<li>
<a href="{% url 'settings:email-setting' %}" class="text-center"><i class="fa fa-envelope"></i> {% trans 'Email setting' %} </a>
</li>
<li>
<a href="{% url 'settings:email-content-setting' %}" class="text-center"><i class="fa fa-file-text"></i> {% trans 'Email content setting' %} </a>
</li>
<li class="active">
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li>
<li>
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
</li>
<li>
<a href="{% url 'settings:security-setting' %}" class="text-center"><i class="fa fa-lock"></i> {% trans 'Security setting' %} </a>
</li>
</ul>
</div> </div>
<div class="tab-content"> <div class="tab-content">
<div class="col-sm-12" style="padding-left:0"> <div class="col-sm-12" style="padding-left:0">
......
...@@ -10,26 +10,7 @@ ...@@ -10,26 +10,7 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="panel-options"> <div class="panel-options">
<ul class="nav nav-tabs"> {% include 'settings/_setting_tabs.html' %}
<li>
<a href="{% url 'settings:basic-setting' %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a>
</li>
<li>
<a href="{% url 'settings:email-setting' %}" class="text-center"><i class="fa fa-envelope"></i> {% trans 'Email setting' %} </a>
</li>
<li>
<a href="{% url 'settings:email-content-setting' %}" class="text-center"><i class="fa fa-file-text"></i> {% trans 'Email content setting' %} </a>
</li>
<li>
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li>
<li>
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
</li>
<li class="active">
<a href="{% url 'settings:security-setting' %}" class="text-center"><i class="fa fa-lock"></i> {% trans 'Security setting' %} </a>
</li>
</ul>
</div> </div>
<div class="tab-content"> <div class="tab-content">
<div class="col-sm-12" style="padding-left:0"> <div class="col-sm-12" style="padding-left:0">
......
...@@ -15,30 +15,7 @@ ...@@ -15,30 +15,7 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="panel-options"> <div class="panel-options">
<ul class="nav nav-tabs"> {% include 'settings/_setting_tabs.html' %}
<li>
<a href="{% url 'settings:basic-setting' %}" class="text-center"><i
class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a>
</li>
<li>
<a href="{% url 'settings:email-setting' %}" class="text-center"><i
class="fa fa-envelope"></i> {% trans 'Email setting' %} </a>
</li>
<li>
<a href="{% url 'settings:email-content-setting' %}" class="text-center"><i class="fa fa-file-text"></i> {% trans 'Email content setting' %} </a>
</li>
<li>
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i
class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li>
<li class="active">
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i
class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
</li>
<li>
<a href="{% url 'settings:security-setting' %}" class="text-center"><i class="fa fa-lock"></i> {% trans 'Security setting' %} </a>
</li>
</ul>
</div> </div>
<div class="tab-content"> <div class="tab-content">
<div class="col-sm-12" style="padding-left:0"> <div class="col-sm-12" style="padding-left:0">
......
/*!
* Ladda
* http://lab.hakim.se/ladda
* MIT licensed
*
* Copyright (C) 2015 Hakim El Hattab, http://hakim.se
*/.ladda-button{position:relative}.ladda-button .ladda-spinner{position:absolute;z-index:2;display:inline-block;width:32px;height:32px;top:50%;margin-top:0;opacity:0;pointer-events:none}.ladda-button .ladda-label{position:relative;z-index:3}.ladda-button .ladda-progress{position:absolute;width:0;height:100%;left:0;top:0;background:rgba(0,0,0,0.2);visibility:hidden;opacity:0;-webkit-transition:0.1s linear all !important;-moz-transition:0.1s linear all !important;-ms-transition:0.1s linear all !important;-o-transition:0.1s linear all !important;transition:0.1s linear all !important}.ladda-button[data-loading] .ladda-progress{opacity:1;visibility:visible}.ladda-button,.ladda-button .ladda-spinner,.ladda-button .ladda-label{-webkit-transition:0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) all !important;-moz-transition:0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) all !important;-ms-transition:0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) all !important;-o-transition:0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) all !important;transition:0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) all !important}.ladda-button[data-style=zoom-in],.ladda-button[data-style=zoom-in] .ladda-spinner,.ladda-button[data-style=zoom-in] .ladda-label,.ladda-button[data-style=zoom-out],.ladda-button[data-style=zoom-out] .ladda-spinner,.ladda-button[data-style=zoom-out] .ladda-label{-webkit-transition:0.3s ease all !important;-moz-transition:0.3s ease all !important;-ms-transition:0.3s ease all !important;-o-transition:0.3s ease all !important;transition:0.3s ease all !important}.ladda-button[data-style=expand-right] .ladda-spinner{right:-6px}.ladda-button[data-style=expand-right][data-size="s"] .ladda-spinner,.ladda-button[data-style=expand-right][data-size="xs"] .ladda-spinner{right:-12px}.ladda-button[data-style=expand-right][data-loading]{padding-right:56px}.ladda-button[data-style=expand-right][data-loading] .ladda-spinner{opacity:1}.ladda-button[data-style=expand-right][data-loading][data-size="s"],.ladda-button[data-style=expand-right][data-loading][data-size="xs"]{padding-right:40px}.ladda-button[data-style=expand-left] .ladda-spinner{left:26px}.ladda-button[data-style=expand-left][data-size="s"] .ladda-spinner,.ladda-button[data-style=expand-left][data-size="xs"] .ladda-spinner{left:4px}.ladda-button[data-style=expand-left][data-loading]{padding-left:56px}.ladda-button[data-style=expand-left][data-loading] .ladda-spinner{opacity:1}.ladda-button[data-style=expand-left][data-loading][data-size="s"],.ladda-button[data-style=expand-left][data-loading][data-size="xs"]{padding-left:40px}.ladda-button[data-style=expand-up]{overflow:hidden}.ladda-button[data-style=expand-up] .ladda-spinner{top:-32px;left:50%;margin-left:0}.ladda-button[data-style=expand-up][data-loading]{padding-top:54px}.ladda-button[data-style=expand-up][data-loading] .ladda-spinner{opacity:1;top:26px;margin-top:0}.ladda-button[data-style=expand-up][data-loading][data-size="s"],.ladda-button[data-style=expand-up][data-loading][data-size="xs"]{padding-top:32px}.ladda-button[data-style=expand-up][data-loading][data-size="s"] .ladda-spinner,.ladda-button[data-style=expand-up][data-loading][data-size="xs"] .ladda-spinner{top:4px}.ladda-button[data-style=expand-down]{overflow:hidden}.ladda-button[data-style=expand-down] .ladda-spinner{top:62px;left:50%;margin-left:0}.ladda-button[data-style=expand-down][data-size="s"] .ladda-spinner,.ladda-button[data-style=expand-down][data-size="xs"] .ladda-spinner{top:40px}.ladda-button[data-style=expand-down][data-loading]{padding-bottom:54px}.ladda-button[data-style=expand-down][data-loading] .ladda-spinner{opacity:1}.ladda-button[data-style=expand-down][data-loading][data-size="s"],.ladda-button[data-style=expand-down][data-loading][data-size="xs"]{padding-bottom:32px}.ladda-button[data-style=slide-left]{overflow:hidden}.ladda-button[data-style=slide-left] .ladda-label{position:relative}.ladda-button[data-style=slide-left] .ladda-spinner{left:100%;margin-left:0}.ladda-button[data-style=slide-left][data-loading] .ladda-label{opacity:0;left:-100%}.ladda-button[data-style=slide-left][data-loading] .ladda-spinner{opacity:1;left:50%}.ladda-button[data-style=slide-right]{overflow:hidden}.ladda-button[data-style=slide-right] .ladda-label{position:relative}.ladda-button[data-style=slide-right] .ladda-spinner{right:100%;margin-left:0;left:16px}.ladda-button[data-style=slide-right][data-loading] .ladda-label{opacity:0;left:100%}.ladda-button[data-style=slide-right][data-loading] .ladda-spinner{opacity:1;left:50%}.ladda-button[data-style=slide-up]{overflow:hidden}.ladda-button[data-style=slide-up] .ladda-label{position:relative}.ladda-button[data-style=slide-up] .ladda-spinner{left:50%;margin-left:0;margin-top:1em}.ladda-button[data-style=slide-up][data-loading] .ladda-label{opacity:0;top:-1em}.ladda-button[data-style=slide-up][data-loading] .ladda-spinner{opacity:1;margin-top:0}.ladda-button[data-style=slide-down]{overflow:hidden}.ladda-button[data-style=slide-down] .ladda-label{position:relative}.ladda-button[data-style=slide-down] .ladda-spinner{left:50%;margin-left:0;margin-top:-2em}.ladda-button[data-style=slide-down][data-loading] .ladda-label{opacity:0;top:1em}.ladda-button[data-style=slide-down][data-loading] .ladda-spinner{opacity:1;margin-top:0}.ladda-button[data-style=zoom-out]{overflow:hidden}.ladda-button[data-style=zoom-out] .ladda-spinner{left:50%;margin-left:32px;-webkit-transform:scale(2.5);-moz-transform:scale(2.5);-ms-transform:scale(2.5);-o-transform:scale(2.5);transform:scale(2.5)}.ladda-button[data-style=zoom-out] .ladda-label{position:relative;display:inline-block}.ladda-button[data-style=zoom-out][data-loading] .ladda-label{opacity:0;-webkit-transform:scale(0.5);-moz-transform:scale(0.5);-ms-transform:scale(0.5);-o-transform:scale(0.5);transform:scale(0.5)}.ladda-button[data-style=zoom-out][data-loading] .ladda-spinner{opacity:1;margin-left:0;-webkit-transform:none;-moz-transform:none;-ms-transform:none;-o-transform:none;transform:none}.ladda-button[data-style=zoom-in]{overflow:hidden}.ladda-button[data-style=zoom-in] .ladda-spinner{left:50%;margin-left:-16px;-webkit-transform:scale(0.2);-moz-transform:scale(0.2);-ms-transform:scale(0.2);-o-transform:scale(0.2);transform:scale(0.2)}.ladda-button[data-style=zoom-in] .ladda-label{position:relative;display:inline-block}.ladda-button[data-style=zoom-in][data-loading] .ladda-label{opacity:0;-webkit-transform:scale(2.2);-moz-transform:scale(2.2);-ms-transform:scale(2.2);-o-transform:scale(2.2);transform:scale(2.2)}.ladda-button[data-style=zoom-in][data-loading] .ladda-spinner{opacity:1;margin-left:0;-webkit-transform:none;-moz-transform:none;-ms-transform:none;-o-transform:none;transform:none}.ladda-button[data-style=contract]{overflow:hidden;width:100px}.ladda-button[data-style=contract] .ladda-spinner{left:50%;margin-left:0}.ladda-button[data-style=contract][data-loading]{border-radius:50%;width:52px}.ladda-button[data-style=contract][data-loading] .ladda-label{opacity:0}.ladda-button[data-style=contract][data-loading] .ladda-spinner{opacity:1}.ladda-button[data-style=contract-overlay]{overflow:hidden;width:100px;box-shadow:0px 0px 0px 2000px transparent}.ladda-button[data-style=contract-overlay] .ladda-spinner{left:50%;margin-left:0}.ladda-button[data-style=contract-overlay][data-loading]{border-radius:50%;width:52px;box-shadow:0px 0px 0px 2000px rgba(0,0,0,0.8)}.ladda-button[data-style=contract-overlay][data-loading] .ladda-label{opacity:0}.ladda-button[data-style=contract-overlay][data-loading] .ladda-spinner{opacity:1}
/*!
* Ladda including the default theme.
*//*!
* Ladda
* http://lab.hakim.se/ladda
* MIT licensed
*
* Copyright (C) 2015 Hakim El Hattab, http://hakim.se
*/.ladda-button{position:relative}.ladda-button .ladda-spinner{position:absolute;z-index:2;display:inline-block;width:32px;height:32px;top:50%;margin-top:0;opacity:0;pointer-events:none}.ladda-button .ladda-label{position:relative;z-index:3}.ladda-button .ladda-progress{position:absolute;width:0;height:100%;left:0;top:0;background:rgba(0,0,0,0.2);visibility:hidden;opacity:0;-webkit-transition:0.1s linear all !important;-moz-transition:0.1s linear all !important;-ms-transition:0.1s linear all !important;-o-transition:0.1s linear all !important;transition:0.1s linear all !important}.ladda-button[data-loading] .ladda-progress{opacity:1;visibility:visible}.ladda-button,.ladda-button .ladda-spinner,.ladda-button .ladda-label{-webkit-transition:0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) all !important;-moz-transition:0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) all !important;-ms-transition:0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) all !important;-o-transition:0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) all !important;transition:0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) all !important}.ladda-button[data-style=zoom-in],.ladda-button[data-style=zoom-in] .ladda-spinner,.ladda-button[data-style=zoom-in] .ladda-label,.ladda-button[data-style=zoom-out],.ladda-button[data-style=zoom-out] .ladda-spinner,.ladda-button[data-style=zoom-out] .ladda-label{-webkit-transition:0.3s ease all !important;-moz-transition:0.3s ease all !important;-ms-transition:0.3s ease all !important;-o-transition:0.3s ease all !important;transition:0.3s ease all !important}.ladda-button[data-style=expand-right] .ladda-spinner{right:-6px}.ladda-button[data-style=expand-right][data-size="s"] .ladda-spinner,.ladda-button[data-style=expand-right][data-size="xs"] .ladda-spinner{right:-12px}.ladda-button[data-style=expand-right][data-loading]{padding-right:56px}.ladda-button[data-style=expand-right][data-loading] .ladda-spinner{opacity:1}.ladda-button[data-style=expand-right][data-loading][data-size="s"],.ladda-button[data-style=expand-right][data-loading][data-size="xs"]{padding-right:40px}.ladda-button[data-style=expand-left] .ladda-spinner{left:26px}.ladda-button[data-style=expand-left][data-size="s"] .ladda-spinner,.ladda-button[data-style=expand-left][data-size="xs"] .ladda-spinner{left:4px}.ladda-button[data-style=expand-left][data-loading]{padding-left:56px}.ladda-button[data-style=expand-left][data-loading] .ladda-spinner{opacity:1}.ladda-button[data-style=expand-left][data-loading][data-size="s"],.ladda-button[data-style=expand-left][data-loading][data-size="xs"]{padding-left:40px}.ladda-button[data-style=expand-up]{overflow:hidden}.ladda-button[data-style=expand-up] .ladda-spinner{top:-32px;left:50%;margin-left:0}.ladda-button[data-style=expand-up][data-loading]{padding-top:54px}.ladda-button[data-style=expand-up][data-loading] .ladda-spinner{opacity:1;top:26px;margin-top:0}.ladda-button[data-style=expand-up][data-loading][data-size="s"],.ladda-button[data-style=expand-up][data-loading][data-size="xs"]{padding-top:32px}.ladda-button[data-style=expand-up][data-loading][data-size="s"] .ladda-spinner,.ladda-button[data-style=expand-up][data-loading][data-size="xs"] .ladda-spinner{top:4px}.ladda-button[data-style=expand-down]{overflow:hidden}.ladda-button[data-style=expand-down] .ladda-spinner{top:62px;left:50%;margin-left:0}.ladda-button[data-style=expand-down][data-size="s"] .ladda-spinner,.ladda-button[data-style=expand-down][data-size="xs"] .ladda-spinner{top:40px}.ladda-button[data-style=expand-down][data-loading]{padding-bottom:54px}.ladda-button[data-style=expand-down][data-loading] .ladda-spinner{opacity:1}.ladda-button[data-style=expand-down][data-loading][data-size="s"],.ladda-button[data-style=expand-down][data-loading][data-size="xs"]{padding-bottom:32px}.ladda-button[data-style=slide-left]{overflow:hidden}.ladda-button[data-style=slide-left] .ladda-label{position:relative}.ladda-button[data-style=slide-left] .ladda-spinner{left:100%;margin-left:0}.ladda-button[data-style=slide-left][data-loading] .ladda-label{opacity:0;left:-100%}.ladda-button[data-style=slide-left][data-loading] .ladda-spinner{opacity:1;left:50%}.ladda-button[data-style=slide-right]{overflow:hidden}.ladda-button[data-style=slide-right] .ladda-label{position:relative}.ladda-button[data-style=slide-right] .ladda-spinner{right:100%;margin-left:0;left:16px}.ladda-button[data-style=slide-right][data-loading] .ladda-label{opacity:0;left:100%}.ladda-button[data-style=slide-right][data-loading] .ladda-spinner{opacity:1;left:50%}.ladda-button[data-style=slide-up]{overflow:hidden}.ladda-button[data-style=slide-up] .ladda-label{position:relative}.ladda-button[data-style=slide-up] .ladda-spinner{left:50%;margin-left:0;margin-top:1em}.ladda-button[data-style=slide-up][data-loading] .ladda-label{opacity:0;top:-1em}.ladda-button[data-style=slide-up][data-loading] .ladda-spinner{opacity:1;margin-top:0}.ladda-button[data-style=slide-down]{overflow:hidden}.ladda-button[data-style=slide-down] .ladda-label{position:relative}.ladda-button[data-style=slide-down] .ladda-spinner{left:50%;margin-left:0;margin-top:-2em}.ladda-button[data-style=slide-down][data-loading] .ladda-label{opacity:0;top:1em}.ladda-button[data-style=slide-down][data-loading] .ladda-spinner{opacity:1;margin-top:0}.ladda-button[data-style=zoom-out]{overflow:hidden}.ladda-button[data-style=zoom-out] .ladda-spinner{left:50%;margin-left:32px;-webkit-transform:scale(2.5);-moz-transform:scale(2.5);-ms-transform:scale(2.5);-o-transform:scale(2.5);transform:scale(2.5)}.ladda-button[data-style=zoom-out] .ladda-label{position:relative;display:inline-block}.ladda-button[data-style=zoom-out][data-loading] .ladda-label{opacity:0;-webkit-transform:scale(0.5);-moz-transform:scale(0.5);-ms-transform:scale(0.5);-o-transform:scale(0.5);transform:scale(0.5)}.ladda-button[data-style=zoom-out][data-loading] .ladda-spinner{opacity:1;margin-left:0;-webkit-transform:none;-moz-transform:none;-ms-transform:none;-o-transform:none;transform:none}.ladda-button[data-style=zoom-in]{overflow:hidden}.ladda-button[data-style=zoom-in] .ladda-spinner{left:50%;margin-left:-16px;-webkit-transform:scale(0.2);-moz-transform:scale(0.2);-ms-transform:scale(0.2);-o-transform:scale(0.2);transform:scale(0.2)}.ladda-button[data-style=zoom-in] .ladda-label{position:relative;display:inline-block}.ladda-button[data-style=zoom-in][data-loading] .ladda-label{opacity:0;-webkit-transform:scale(2.2);-moz-transform:scale(2.2);-ms-transform:scale(2.2);-o-transform:scale(2.2);transform:scale(2.2)}.ladda-button[data-style=zoom-in][data-loading] .ladda-spinner{opacity:1;margin-left:0;-webkit-transform:none;-moz-transform:none;-ms-transform:none;-o-transform:none;transform:none}.ladda-button[data-style=contract]{overflow:hidden;width:100px}.ladda-button[data-style=contract] .ladda-spinner{left:50%;margin-left:0}.ladda-button[data-style=contract][data-loading]{border-radius:50%;width:52px}.ladda-button[data-style=contract][data-loading] .ladda-label{opacity:0}.ladda-button[data-style=contract][data-loading] .ladda-spinner{opacity:1}.ladda-button[data-style=contract-overlay]{overflow:hidden;width:100px;box-shadow:0px 0px 0px 2000px transparent}.ladda-button[data-style=contract-overlay] .ladda-spinner{left:50%;margin-left:0}.ladda-button[data-style=contract-overlay][data-loading]{border-radius:50%;width:52px;box-shadow:0px 0px 0px 2000px rgba(0,0,0,0.8)}.ladda-button[data-style=contract-overlay][data-loading] .ladda-label{opacity:0}.ladda-button[data-style=contract-overlay][data-loading] .ladda-spinner{opacity:1}.ladda-button{background:#666;border:0;padding:14px 18px;font-size:18px;cursor:pointer;color:#fff;border-radius:2px;border:1px solid transparent;-webkit-appearance:none;-webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:transparent}.ladda-button:hover{border-color:rgba(0,0,0,0.07);background-color:#888}.ladda-button[data-color=green]{background:#2aca76}.ladda-button[data-color=green]:hover{background-color:#38d683}.ladda-button[data-color=blue]{background:#53b5e6}.ladda-button[data-color=blue]:hover{background-color:#69bfe9}.ladda-button[data-color=red]{background:#ea8557}.ladda-button[data-color=red]:hover{background-color:#ed956e}.ladda-button[data-color=purple]{background:#9973C2}.ladda-button[data-color=purple]:hover{background-color:#a685ca}.ladda-button[data-color=mint]{background:#16a085}.ladda-button[data-color=mint]:hover{background-color:#19b698}.ladda-button[disabled],.ladda-button[data-loading]{border-color:rgba(0,0,0,0.07)}.ladda-button[disabled],.ladda-button[disabled]:hover,.ladda-button[data-loading],.ladda-button[data-loading]:hover{cursor:default;background-color:#999}.ladda-button[data-size=xs]{padding:4px 8px}.ladda-button[data-size=xs] .ladda-label{font-size:0.7em}.ladda-button[data-size=s]{padding:6px 10px}.ladda-button[data-size=s] .ladda-label{font-size:0.9em}.ladda-button[data-size=l] .ladda-label{font-size:1.2em}.ladda-button[data-size=xl] .ladda-label{font-size:1.5em}
...@@ -177,7 +177,7 @@ function formSubmit(props) { ...@@ -177,7 +177,7 @@ function formSubmit(props) {
*/ */
props = props || {}; props = props || {};
var data = props.data || props.form.serializeObject(); var data = props.data || props.form.serializeObject();
var redirect_to = props.redirect_to; var redirectTo = props.redirect_to || props.redirectTo;
$.ajax({ $.ajax({
url: props.url, url: props.url,
type: props.method || 'POST', type: props.method || 'POST',
...@@ -185,12 +185,8 @@ function formSubmit(props) { ...@@ -185,12 +185,8 @@ function formSubmit(props) {
contentType: props.content_type || "application/json; charset=utf-8", contentType: props.content_type || "application/json; charset=utf-8",
dataType: props.data_type || "json" dataType: props.data_type || "json"
}).done(function (data, textState, jqXHR) { }).done(function (data, textState, jqXHR) {
if (redirect_to) { if (redirectTo) {
if (props.message) { location.href = redirectTo;
var messages = "ed65330a45559c87345a0eb6ac7812d18d0d8976$[[\"__json_message\"\0540\05425\054\"asdfasdf \\u521b\\u5efa\\u6210\\u529f\"]]"
setCookie("messages", messages)
}
location.href = redirect_to;
} else if (typeof props.success === 'function') { } else if (typeof props.success === 'function') {
return props.success(data, textState, jqXHR); return props.success(data, textState, jqXHR);
} }
...@@ -254,7 +250,6 @@ function formSubmit(props) { ...@@ -254,7 +250,6 @@ function formSubmit(props) {
} }
$('.has-error').get(0).scrollIntoView(); $('.has-error').get(0).scrollIntoView();
} }
}) })
} }
...@@ -1032,6 +1027,62 @@ function rootNodeAddDom(ztree, callback) { ...@@ -1032,6 +1027,62 @@ function rootNodeAddDom(ztree, callback) {
}) })
} }
function APIExportCSV(props) {
/*
{
listUrl:
objectsId:
template:
table:
params:
}
*/
var _listUrl = props.listUrl;
var _objectsId = props.objectsId;
var _template = props.template;
var _table = props.table;
var _params = props.params || {};
var tableParams = _table.ajax.params();
var exportUrl = setUrlParam(_listUrl, 'format', 'csv');
if (_template) {
exportUrl = setUrlParam(exportUrl, 'template', _template)
}
for (var k in tableParams) {
if (datatableInternalParams.includes(k)) {
continue
}
if (!tableParams[k]) {
continue
}
exportUrl = setUrlParam(exportUrl, k, tableParams[k])
}
for (var k in _params) {
exportUrl = setUrlParam(exportUrl, k, tableParams[k])
}
if (!_objectsId) {
console.log(exportUrl);
window.open(exportUrl);
return
}
requestApi({
url: '/api/v1/common/resources/cache/',
data: JSON.stringify({resources: _objectsId}),
method: "POST",
flash_message: false,
success: function (data) {
exportUrl = setUrlParam(exportUrl, 'spm', data.spm);
console.log(exportUrl);
window.open(exportUrl);
},
failed: function () {
toastr.error(gettext('Export failed'));
}
});
}
function APIExportData(props) { function APIExportData(props) {
props = props || {}; props = props || {};
$.ajax({ $.ajax({
...@@ -1081,6 +1132,7 @@ function APIImportData(props) { ...@@ -1081,6 +1132,7 @@ function APIImportData(props) {
}, },
error: function (error) { error: function (error) {
var data = error.responseJSON; var data = error.responseJSON;
console.log(data);
if (data instanceof Array) { if (data instanceof Array) {
var html = ''; var html = '';
var li = ''; var li = '';
...@@ -1141,8 +1193,8 @@ function objectAttrsIsBool(obj, attrs) { ...@@ -1141,8 +1193,8 @@ function objectAttrsIsBool(obj, attrs) {
attrs.forEach(function (attr) { attrs.forEach(function (attr) {
if (!obj[attr]) { if (!obj[attr]) {
obj[attr] = false obj[attr] = false
} else if (['on', '1'].includes(obj[attr])) { } else {
obj[attr] = true obj[attr] = ['on', '1', 'true', 'True'].includes(obj[attr]);
} }
}) })
} }
......
/*!
* Ladda for jQuery
* http://lab.hakim.se/ladda
* MIT licensed
*
* Copyright (C) 2015 Hakim El Hattab, http://hakim.se
*/
!function(a,b){if(void 0===b)return console.error("jQuery required for Ladda.jQuery");var c=[];b=b.extend(b,{ladda:function(b){"stopAll"===b&&a.stopAll()}}),b.fn=b.extend(b.fn,{ladda:function(d){var e=c.slice.call(arguments,1);return"bind"===d?(e.unshift(b(this).selector),a.bind.apply(a,e)):b(this).each(function(){var c,f=b(this);void 0===d?f.data("ladda",a.create(this)):(c=f.data("ladda"),c[d].apply(c,e))}),this}})}(this.Ladda,this.jQuery);
\ No newline at end of file
/*!
* Ladda 1.0.0 (2016-03-08, 09:31)
* http://lab.hakim.se/ladda
* MIT licensed
*
* Copyright (C) 2016 Hakim El Hattab, http://hakim.se
*/
!function(a,b){"object"==typeof exports?module.exports=b(require("spin.js")):"function"==typeof define&&define.amd?define(["spin"],b):a.Ladda=b(a.Spinner)}(this,function(a){"use strict";function b(a){if("undefined"==typeof a)return void console.warn("Ladda button target must be defined.");if(/ladda-button/i.test(a.className)||(a.className+=" ladda-button"),a.hasAttribute("data-style")||a.setAttribute("data-style","expand-right"),!a.querySelector(".ladda-label")){var b=document.createElement("span");b.className="ladda-label",i(a,b)}var c,d=a.querySelector(".ladda-spinner");d||(d=document.createElement("span"),d.className="ladda-spinner"),a.appendChild(d);var e,f={start:function(){return c||(c=g(a)),a.setAttribute("disabled",""),a.setAttribute("data-loading",""),clearTimeout(e),c.spin(d),this.setProgress(0),this},startAfter:function(a){return clearTimeout(e),e=setTimeout(function(){f.start()},a),this},stop:function(){return a.removeAttribute("disabled"),a.removeAttribute("data-loading"),clearTimeout(e),c&&(e=setTimeout(function(){c.stop()},1e3)),this},toggle:function(){return this.isLoading()?this.stop():this.start(),this},setProgress:function(b){b=Math.max(Math.min(b,1),0);var c=a.querySelector(".ladda-progress");0===b&&c&&c.parentNode?c.parentNode.removeChild(c):(c||(c=document.createElement("div"),c.className="ladda-progress",a.appendChild(c)),c.style.width=(b||0)*a.offsetWidth+"px")},enable:function(){return this.stop(),this},disable:function(){return this.stop(),a.setAttribute("disabled",""),this},isLoading:function(){return a.hasAttribute("data-loading")},remove:function(){clearTimeout(e),a.removeAttribute("disabled",""),a.removeAttribute("data-loading",""),c&&(c.stop(),c=null);for(var b=0,d=j.length;d>b;b++)if(f===j[b]){j.splice(b,1);break}}};return j.push(f),f}function c(a,b){for(;a.parentNode&&a.tagName!==b;)a=a.parentNode;return b===a.tagName?a:void 0}function d(a){for(var b=["input","textarea","select"],c=[],d=0;d<b.length;d++)for(var e=a.getElementsByTagName(b[d]),f=0;f<e.length;f++)e[f].hasAttribute("required")&&c.push(e[f]);return c}function e(a,e){e=e||{};var f=[];"string"==typeof a?f=h(document.querySelectorAll(a)):"object"==typeof a&&"string"==typeof a.nodeName&&(f=[a]);for(var g=0,i=f.length;i>g;g++)!function(){var a=f[g];if("function"==typeof a.addEventListener){var h=b(a),i=-1;a.addEventListener("click",function(b){var f=!0,g=c(a,"FORM");if("undefined"!=typeof g)if("function"==typeof g.checkValidity)f=g.checkValidity();else for(var j=d(g),k=0;k<j.length;k++)""===j[k].value.replace(/^\s+|\s+$/g,"")&&(f=!1),"checkbox"!==j[k].type&&"radio"!==j[k].type||j[k].checked||(f=!1),"email"===j[k].type&&(f=/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/.test(j[k].value));f&&(h.startAfter(1),"number"==typeof e.timeout&&(clearTimeout(i),i=setTimeout(h.stop,e.timeout)),"function"==typeof e.callback&&e.callback.apply(null,[h]))},!1)}}()}function f(){for(var a=0,b=j.length;b>a;a++)j[a].stop()}function g(b){var c,d,e=b.offsetHeight;0===e&&(e=parseFloat(window.getComputedStyle(b).height)),e>32&&(e*=.8),b.hasAttribute("data-spinner-size")&&(e=parseInt(b.getAttribute("data-spinner-size"),10)),b.hasAttribute("data-spinner-color")&&(c=b.getAttribute("data-spinner-color")),b.hasAttribute("data-spinner-lines")&&(d=parseInt(b.getAttribute("data-spinner-lines"),10));var f=.2*e,g=.6*f,h=7>f?2:3;return new a({color:c||"#fff",lines:d||12,radius:f,length:g,width:h,zIndex:"auto",top:"auto",left:"auto",className:""})}function h(a){for(var b=[],c=0;c<a.length;c++)b.push(a[c]);return b}function i(a,b){var c=document.createRange();c.selectNodeContents(a),c.surroundContents(b),a.appendChild(b)}var j=[];return{bind:e,create:b,stopAll:f}});
\ No newline at end of file
!function(a,b){"object"==typeof exports?module.exports=b():"function"==typeof define&&define.amd?define(b):a.Spinner=b()}(this,function(){"use strict";function a(a,b){var c,d=document.createElement(a||"div");for(c in b)d[c]=b[c];return d}function b(a){for(var b=1,c=arguments.length;c>b;b++)a.appendChild(arguments[b]);return a}function c(a,b,c,d){var e=["opacity",b,~~(100*a),c,d].join("-"),f=.01+c/d*100,g=Math.max(1-(1-a)/b*(100-f),a),h=j.substring(0,j.indexOf("Animation")).toLowerCase(),i=h&&"-"+h+"-"||"";return l[e]||(m.insertRule("@"+i+"keyframes "+e+"{0%{opacity:"+g+"}"+f+"%{opacity:"+a+"}"+(f+.01)+"%{opacity:1}"+(f+b)%100+"%{opacity:"+a+"}100%{opacity:"+g+"}}",m.cssRules.length),l[e]=1),e}function d(a,b){var c,d,e=a.style;for(b=b.charAt(0).toUpperCase()+b.slice(1),d=0;d<k.length;d++)if(c=k[d]+b,void 0!==e[c])return c;return void 0!==e[b]?b:void 0}function e(a,b){for(var c in b)a.style[d(a,c)||c]=b[c];return a}function f(a){for(var b=1;b<arguments.length;b++){var c=arguments[b];for(var d in c)void 0===a[d]&&(a[d]=c[d])}return a}function g(a,b){return"string"==typeof a?a:a[b%a.length]}function h(a){this.opts=f(a||{},h.defaults,n)}function i(){function c(b,c){return a("<"+b+' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">',c)}m.addRule(".spin-vml","behavior:url(#default#VML)"),h.prototype.lines=function(a,d){function f(){return e(c("group",{coordsize:k+" "+k,coordorigin:-j+" "+-j}),{width:k,height:k})}function h(a,h,i){b(m,b(e(f(),{rotation:360/d.lines*a+"deg",left:~~h}),b(e(c("roundrect",{arcsize:d.corners}),{width:j,height:d.width,left:d.radius,top:-d.width>>1,filter:i}),c("fill",{color:g(d.color,a),opacity:d.opacity}),c("stroke",{opacity:0}))))}var i,j=d.length+d.width,k=2*j,l=2*-(d.width+d.length)+"px",m=e(f(),{position:"absolute",top:l,left:l});if(d.shadow)for(i=1;i<=d.lines;i++)h(i,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(i=1;i<=d.lines;i++)h(i);return b(a,m)},h.prototype.opacity=function(a,b,c,d){var e=a.firstChild;d=d.shadow&&d.lines||0,e&&b+d<e.childNodes.length&&(e=e.childNodes[b+d],e=e&&e.firstChild,e=e&&e.firstChild,e&&(e.opacity=c))}}var j,k=["webkit","Moz","ms","O"],l={},m=function(){var c=a("style",{type:"text/css"});return b(document.getElementsByTagName("head")[0],c),c.sheet||c.styleSheet}(),n={lines:12,length:7,width:5,radius:10,rotate:0,corners:1,color:"#000",direction:1,speed:1,trail:100,opacity:.25,fps:20,zIndex:2e9,className:"spinner",top:"50%",left:"50%",position:"absolute"};h.defaults={},f(h.prototype,{spin:function(b){this.stop();var c=this,d=c.opts,f=c.el=e(a(0,{className:d.className}),{position:d.position,width:0,zIndex:d.zIndex});d.radius+d.length+d.width;if(e(f,{left:d.left,top:d.top}),b&&b.insertBefore(f,b.firstChild||null),f.setAttribute("role","progressbar"),c.lines(f,c.opts),!j){var g,h=0,i=(d.lines-1)*(1-d.direction)/2,k=d.fps,l=k/d.speed,m=(1-d.opacity)/(l*d.trail/100),n=l/d.lines;!function o(){h++;for(var a=0;a<d.lines;a++)g=Math.max(1-(h+(d.lines-a)*n)%l*m,d.opacity),c.opacity(f,a*d.direction+i,g,d);c.timeout=c.el&&setTimeout(o,~~(1e3/k))}()}return c},stop:function(){var a=this.el;return a&&(clearTimeout(this.timeout),a.parentNode&&a.parentNode.removeChild(a),this.el=void 0),this},lines:function(d,f){function h(b,c){return e(a(),{position:"absolute",width:f.length+f.width+"px",height:f.width+"px",background:b,boxShadow:c,transformOrigin:"left",transform:"rotate("+~~(360/f.lines*k+f.rotate)+"deg) translate("+f.radius+"px,0)",borderRadius:(f.corners*f.width>>1)+"px"})}for(var i,k=0,l=(f.lines-1)*(1-f.direction)/2;k<f.lines;k++)i=e(a(),{position:"absolute",top:1+~(f.width/2)+"px",transform:f.hwaccel?"translate3d(0,0,0)":"",opacity:f.opacity,animation:j&&c(f.opacity,f.trail,l+k*f.direction,f.lines)+" "+1/f.speed+"s linear infinite"}),f.shadow&&b(i,e(h("#000","0 0 4px #000"),{top:"2px"})),b(d,b(i,h(g(f.color,k),"0 0 1px rgba(0,0,0,.1)")));return d},opacity:function(a,b,c){b<a.childNodes.length&&(a.childNodes[b].style.opacity=c)}});var o=e(a("group"),{behavior:"url(#default#VML)"});return!d(o,"transform")&&o.adj?i():j=d(o,"animation"),h});
\ No newline at end of file
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block help_message %}
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-sm-3" id="split-left" style="padding-left: 3px;padding-right: 0">
{% include 'assets/_node_tree.html' %}
</div>
<div class="col-sm-9 animated fadeInRight" id="split-right">
<div class="tree-toggle" style="z-index: 10">
<div class="btn btn-sm btn-primary tree-toggle-btn" onclick="toggleSpliter()">
<i class="fa fa-angle-left fa-x" id="toggle-icon"></i>
</div>
</div>
<div class="mail-box-header">
{% block table_container %}
<table class="table table-striped table-bordered table-hover" id="{% block table_id %}editable{% endblock %}" >
<thead>
<tr>
{% block table_head %} {% endblock %}
</tr>
</thead>
<tbody>
{% block table_body %} {% endblock %}
</tbody>
</table>
{% endblock %}
</div>
</div>
</div>
</div>
<script>
var showTree = 1;
function toggleSpliter() {
if (showTree === 1) {
$("#split-left").hide(500, function () {
$("#split-right").attr("class", "col-sm-12");
$("#toggle-icon").attr("class", "fa fa-angle-right fa-x");
showTree = 1;
});
} else {
console.log("hide")
$("#split-right").attr("class", "col-sm-9");
$("#toggle-icon").attr("class", "fa fa-angle-left fa-x");
$("#split-left").show(500);
showTree = 0;
}
}
</script>
{% endblock %}
{% load static %}
{% load i18n %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="{{ FAVICON_URL }}" type="image/x-icon">
<title>{% block html_title %}{% endblock %}</title>
{% include '_head_css_js.html' %}
<link href="{% static "css/jumpserver.css" %}" rel="stylesheet">
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
<script src="{% static "js/jumpserver.js" %}"></script>
<style>
.passwordBox {
max-width: 560px;
margin: 0 auto;
padding: 100px 20px 20px 20px;
}
</style>
{% block custom_head_css_js %} {% endblock %}
</head>
<body class="gray-bg">
<div class="passwordBox animated fadeInDown">
<div class="row">
<div class="col-md-12">
<div class="ibox-content">
<img src="{{ LOGO_URL }}" style="margin: auto" width="50" height="50">
<h2 class="font-bold" style="display: inline">{% block title %}{% endblock %}</h2>
<h1></h1>
{% block content %} {% endblock %}
</div>
</div>
</div>
<hr/>
<div class="row">
<div class="col-md-12">
{% include '_copyright.html' %}
</div>
</div>
</div>
</body>
{% block custom_foot_js %} {% endblock %}
</html>
{% load i18n %}
<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 id="li_csv_export">
<a id="btn_csv_export" tabindex="0">
<span>{% trans "Export" %}</span>
</a>
</li>
<li id="li_csv_import">
<a id="btn_csv_import" data-toggle="modal" data-target="#csv_import_modal" tabindex="0">
<span>{% trans "Import" %}</span>
</a>
</li>
<li id="li_csv_update">
<a id="btn_csv_update" data-toggle="modal" data-target="#csv_update_modal" tabindex="0">
<span>{% trans "Update" %}</span>
</a>
</li>
</ul>
</div>
</div>
{% include '_csv_import_modal.html' %}
{% include '_csv_update_modal.html' %}
<script>
var csvTable = null;
var csvListUrl = null;
var csvExportCallback = null;
function initCsvImportExport(table, objectType, listUrl, hide) {
csvTable = table;
$(".csv_object_type").html(objectType);
csvListUrl = listUrl ? listUrl : csvTable.ajax.url();
if (hide && hide.length > 0) {
hide.forEach(function (v) {
$("#li_csv_" + v).hide();
})
}
}
var datatableInternalParams = ['draw', 'limit', 'order', 'offset'];
$(document).ready(function () {
}).on('click', '#btn_csv_export', function () {
var selectedObjects = csvTable.selected;
function _export() {
APIExportCSV({
listUrl: csvListUrl,
objectsId: selectedObjects,
table: csvTable
});
}
if (csvExportCallback) {
csvExportCallback(_export)
} else {
_export();
}
})
</script>
{% extends '_modal.html' %}
{% load i18n %}
{% block modal_id %}csv_import_modal{% endblock %}
{% block modal_title%}<span class="csv_object_type">csv</span> {% trans 'Import' %}{% endblock %}
{% block modal_confirm_id %}btn_csv_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 id="csv_download_template" 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_csv_file" type="file" name="file" />
</div>
</form>
<div style="max-height: 300px;overflow: auto">
<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>
<script>
$(document).ready(function () {
}).on("click", '#csv_download_template', function () {
var theUrl ="csvImportURL?format=csv&template=import&limit=1" ;
theUrl = theUrl.replace("csvImportURL", csvListUrl);
window.open(theUrl)
}).on('click', '#btn_csv_import_confirm', function () {
var file = document.getElementById('id_csv_file').files[0];
if(!file){
toastr.error("{% trans "Please select file" %}");
return
}
APIImportData({
url: csvListUrl,
method: "POST",
body: file,
data_table: csvTable
});
})
</script>
{% endblock %}
{% extends '_modal.html' %}
{% load i18n %}
{% block modal_id %}csv_update_modal{% endblock %}
{% block modal_confirm_id %}btn_csv_update_confirm{% endblock %}
{% block modal_title%}<span class="csv_object_type">csv</span> {% trans 'Update' %}{% 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="csv_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="csv_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>
<script>
$(document).ready(function () {
}).on('click', '#csv_download_update_template', function () {
var objectsId = csvTable.selected;
APIExportCSV({
listUrl: csvListUrl,
objectsId: objectsId,
template: 'update',
table: csvTable
});
}).on('click', '#btn_csv_update_confirm', function () {
var file = document.getElementById('csv_update_file').files[0];
if(!file){
toastr.error("{% trans "Please select file" %}");
return
}
APIImportData({
url: csvListUrl,
method: "PUT",
body: file,
data_table: csvTable
});
})
</script>
{% endblock %}
...@@ -45,6 +45,9 @@ ...@@ -45,6 +45,9 @@
<li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li> <li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li>
<li id="label"><a href="{% url 'assets:label-list' %}">{% trans 'Labels' %}</a></li> <li id="label"><a href="{% url 'assets:label-list' %}">{% trans 'Labels' %}</a></li>
<li id="cmd-filter"><a href="{% url 'assets:cmd-filter-list' %}">{% trans 'Command filters' %}</a></li> <li id="cmd-filter"><a href="{% url 'assets:cmd-filter-list' %}">{% trans 'Command filters' %}</a></li>
{% if request.user.is_superuser %}
<li id="platform"><a href="{% url 'assets:platform-list' %}">{% trans 'Platform list' %}</a></li>
{% endif %}
</ul> </ul>
</li> </li>
{% endif %} {% endif %}
......
{% load static %}
{% load i18n %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title> {{ JMS_TITLE }} </title>
<link rel="shortcut icon" href="{{ FAVICON_URL }}" type="image/x-icon">
{# <link rel="stylesheet" href="{% static 'fonts/font_otp/iconfont.css' %}" />#}
<link rel="stylesheet" href="{% static 'css/otp.css' %}" />
<script src="{% static 'js/jquery-3.1.1.min.js' %}"></script>
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
</head>
<body>
<header>
<div class="logo">
<a href="{% url 'index' %}">
<img src="{{ LOGO_URL }}" alt="" width="50px" height="50px"/>
</a>
<a href="{% url 'index' %}">{{ JMS_TITLE }}</a>
</div>
<div>
<a href="{% url 'index' %}">{% trans 'Home page' %}</a>
<b></b>
<a href="http://docs.jumpserver.org/zh/docs/">{% trans 'Docs' %}</a>
<b></b>
<a href="https://www.github.com/jumpserver/">GitHub</a>
</div>
</header>
<body>
{% block body %}
{% endblock %}
</body>
<footer>
<div class="" style="margin-top: 100px;">
{% include '_copyright.html' %}
</div>
</footer>
</body>
</html>
{% load i18n %} {% extends '_base_only_content.html' %}
{% load static %} {% load static %}
<!DOCTYPE html> {% load i18n %}
<html> {% block html_title %} {{ title }} {% endblock %}
{% block title %} {{ title }}{% endblock %}
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
{% include '_head_css_js.html' %}
<link href="{% static "css/jumpserver.css" %}" rel="stylesheet">
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
<script src="{% static "js/jumpserver.js" %}"></script>
</head> {% block custom_head_css_js %}
<style>
.passwordBox {
max-width: 660px;
}
</style>
{% endblock %}
<body class="gray-bg"> {% block content %}
<div class="passwordBox2 animated fadeInDown"> <div>
<div class="row"> {% if errors %}
<div class="col-md-12"> <p>
<div class="ibox-content"> <div class="alert alert-danger">
<div> {{ errors }}
<img src="{{ LOGO_URL }}" style="margin: auto" width="82" height="82"> </div>
<h2 style="display: inline"> </p>
{{ JMS_TITLE }} {% endif %}
</h2>
</div>
{% if errors %}
<p>
<div class="alert alert-danger">
{{ errors }}
</div>
</p>
{% endif %}
{% if messages %} {% if messages %}
<p> <p>
<div class="alert alert-success" id="messages"> <div class="alert alert-success" id="messages">
{{ messages|safe }} {{ messages|safe }}
</div>
</p>
{% endif %}
<div class="row">
<div class="col-lg-3">
<a href="{{ redirect_url }}" class="btn btn-primary block full-width m-b">{% trans 'Return' %}</a>
</div>
</div>
</div>
</div> </div>
</div> </p>
<hr/> {% endif %}
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-lg-3">
{% include '_copyright.html' %} <a href="{{ redirect_url }}" class="btn btn-primary block full-width m-b">{% trans 'Return' %}</a>
</div> </div>
</div> </div>
</div> </div>
</body> {% endblock %}
{% block custom_foot_js %}
<script> <script>
var time = '{{ interval }}'; var time = '{{ interval }}';
if (!time){ if (!time) {
time = 5; time = 5;
} else { } else {
time = parseInt(time); time = parseInt(time);
} }
function redirect_page() { function redirect_page() {
if (time >= 0) { if (time >= 0) {
var messages = '{{ messages|safe }}, <b>' + time +'</b> ...'; var messages = '{{ messages|safe }}, <b>' + time + '</b> ...';
$('#messages').html(messages); $('#messages').html(messages);
time--; time--;
setTimeout(redirect_page, 1000); setTimeout(redirect_page, 1000);
} } else {
else {
window.location.href = "{{ redirect_url }}"; window.location.href = "{{ redirect_url }}";
} }
} }
{% if auto_redirect %} {% if auto_redirect %}
window.onload = redirect_page; window.onload = redirect_page;
{% endif %} {% endif %}
</script> </script>
</html> {% endblock %}
# Generated by Django 2.2.7 on 2019-12-06 02:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('terminal', '0018_auto_20191202_1010'),
]
operations = [
migrations.AlterField(
model_name='replaystorage',
name='type',
field=models.CharField(choices=[('null', 'Null'), ('server', 'Server'), ('s3', 'S3'), ('ceph', 'Ceph'), ('swift', 'Swift'), ('oss', 'OSS'), ('azure', 'Azure')], default='server', max_length=16, verbose_name='Type'),
),
]
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from ..serializers import ( from ..serializers import UserGroupSerializer
UserGroupSerializer,
UserGroupListSerializer,
UserGroupUpdateMemberSerializer,
)
from ..models import UserGroup from ..models import UserGroup
from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins.api import OrgBulkModelViewSet
from orgs.mixins import generics
from common.permissions import IsOrgAdmin from common.permissions import IsOrgAdmin
__all__ = ['UserGroupViewSet', 'UserGroupUpdateUserApi'] __all__ = ['UserGroupViewSet']
class UserGroupViewSet(OrgBulkModelViewSet): class UserGroupViewSet(OrgBulkModelViewSet):
model = UserGroup model = UserGroup
filter_fields = ("name",) filter_fields = ("name",)
search_fields = filter_fields search_fields = filter_fields
serializer_class = UserGroupSerializer
permission_classes = (IsOrgAdmin,) permission_classes = (IsOrgAdmin,)
serializer_class = UserGroupSerializer
def get_serializer_class(self):
if self.action in ("list", 'retrieve') and \
self.request.query_params.get("display"):
return UserGroupListSerializer
return self.serializer_class
class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView):
model = UserGroup
serializer_class = UserGroupUpdateMemberSerializer
permission_classes = (IsOrgAdmin,)
...@@ -24,7 +24,7 @@ from ..signals import post_user_create ...@@ -24,7 +24,7 @@ from ..signals import post_user_create
logger = get_logger(__name__) logger = get_logger(__name__)
__all__ = [ __all__ = [
'UserViewSet', 'UserChangePasswordApi', 'UserUpdateGroupApi', 'UserViewSet', 'UserChangePasswordApi',
'UserResetPasswordApi', 'UserResetPKApi', 'UserUpdatePKApi', 'UserResetPasswordApi', 'UserResetPKApi', 'UserUpdatePKApi',
'UserUnblockPKApi', 'UserProfileApi', 'UserResetOTPApi', 'UserUnblockPKApi', 'UserProfileApi', 'UserResetOTPApi',
] ]
...@@ -39,8 +39,10 @@ class UserQuerysetMixin: ...@@ -39,8 +39,10 @@ class UserQuerysetMixin:
class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet): class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
filter_fields = ('username', 'email', 'name', 'id') filter_fields = ('username', 'email', 'name', 'id')
search_fields = filter_fields search_fields = filter_fields
serializer_class = serializers.UserSerializer serializer_classes = {
serializer_display_class = serializers.UserDisplaySerializer 'default': serializers.UserSerializer,
'display': serializers.UserDisplaySerializer
}
permission_classes = (IsOrgAdmin, CanUpdateDeleteUser) permission_classes = (IsOrgAdmin, CanUpdateDeleteUser)
def get_queryset(self): def get_queryset(self):
...@@ -99,11 +101,6 @@ class UserChangePasswordApi(UserQuerysetMixin, generics.RetrieveUpdateAPIView): ...@@ -99,11 +101,6 @@ class UserChangePasswordApi(UserQuerysetMixin, generics.RetrieveUpdateAPIView):
user.save() user.save()
class UserUpdateGroupApi(UserQuerysetMixin, generics.RetrieveUpdateAPIView):
serializer_class = serializers.UserUpdateGroupSerializer
permission_classes = (IsOrgAdmin,)
class UserResetPasswordApi(UserQuerysetMixin, generics.UpdateAPIView): class UserResetPasswordApi(UserQuerysetMixin, generics.UpdateAPIView):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = serializers.UserSerializer serializer_class = serializers.UserSerializer
......
# -*- coding: utf-8 -*-
#
from .user import *
from .group import *
from .profile import *
# -*- coding: utf-8 -*-
#
from django import forms
from django.utils.translation import gettext_lazy as _
from orgs.mixins.forms import OrgModelForm
from ..models import User, UserGroup
__all__ = ['UserGroupForm']
class UserGroupForm(OrgModelForm):
users = forms.ModelMultipleChoiceField(
queryset=User.objects.none(),
label=_("User"),
widget=forms.SelectMultiple(
attrs={
'class': 'users-select2',
'data-placeholder': _('Select users')
}
),
required=False,
)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.set_fields_queryset()
def set_fields_queryset(self):
users_field = self.fields.get('users')
if self.instance:
users_field.initial = self.instance.users.all()
users_field.queryset = self.instance.users.all()
else:
users_field.queryset = User.objects.none()
def save(self, commit=True):
raise Exception("Save by restful api")
class Meta:
model = UserGroup
fields = [
'name', 'users', 'comment',
]
# -*- coding: utf-8 -*-
#
from django import forms
from django.utils.translation import gettext_lazy as _
from captcha.fields import CaptchaField
from common.utils import validate_ssh_public_key
from ..models import User
__all__ = [
'UserProfileForm', 'UserMFAForm', 'UserFirstLoginFinishForm',
'UserPasswordForm', 'UserPublicKeyForm', 'FileForm',
'UserTokenResetPasswordForm', 'UserForgotPasswordForm',
]
class UserProfileForm(forms.ModelForm):
username = forms.CharField(disabled=True, label=_("Username"))
name = forms.CharField(disabled=True, label=_("Name"))
email = forms.CharField(disabled=True)
class Meta:
model = User
fields = [
'username', 'name', 'email',
'wechat', 'phone',
]
UserProfileForm.verbose_name = _("Profile")
class UserMFAForm(forms.ModelForm):
mfa_description = _(
'When enabled, '
'you will enter the MFA binding process the next time you log in. '
'you can also directly bind in '
'"personal information -> quick modification -> change MFA Settings"!')
class Meta:
model = User
fields = ['mfa_level']
widgets = {'mfa_level': forms.RadioSelect()}
help_texts = {
'mfa_level': _('* Enable MFA authentication '
'to make the account more secure.'),
}
UserMFAForm.verbose_name = _("MFA")
class UserFirstLoginFinishForm(forms.Form):
finish_description = _(
'In order to protect you and your company, '
'please keep your account, '
'password and key sensitive information properly. '
'(for example: setting complex password, enabling MFA authentication)'
)
UserFirstLoginFinishForm.verbose_name = _("Finish")
class UserTokenResetPasswordForm(forms.Form):
new_password = forms.CharField(
min_length=5, max_length=128,
widget=forms.PasswordInput,
label=_("New password")
)
confirm_password = forms.CharField(
min_length=5, max_length=128,
widget=forms.PasswordInput,
label=_("Confirm password")
)
def clean_confirm_password(self):
new_password = self.cleaned_data['new_password']
confirm_password = self.cleaned_data['confirm_password']
if new_password != confirm_password:
raise forms.ValidationError(_('Password does not match'))
return confirm_password
class UserForgotPasswordForm(forms.Form):
email = forms.EmailField(label=_("Email"))
captcha = CaptchaField(label=_("Captcha"))
class UserPasswordForm(UserTokenResetPasswordForm):
old_password = forms.CharField(
max_length=128, widget=forms.PasswordInput,
label=_("Old password")
)
def __init__(self, *args, **kwargs):
self.instance = kwargs.pop('instance')
super().__init__(*args, **kwargs)
def clean_old_password(self):
old_password = self.cleaned_data['old_password']
if not self.instance.check_password(old_password):
raise forms.ValidationError(_('Old password error'))
return old_password
def save(self):
password = self.cleaned_data['new_password']
self.instance.reset_password(new_password=password)
return self.instance
class UserPublicKeyForm(forms.Form):
pubkey_description = _('Automatically configure and download the SSH key')
public_key = forms.CharField(
label=_('ssh public key'), max_length=5000, required=False,
widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}),
help_text=_('Paste your id_rsa.pub here.')
)
def __init__(self, *args, **kwargs):
if 'instance' in kwargs:
self.instance = kwargs.pop('instance')
else:
self.instance = None
super().__init__(*args, **kwargs)
def clean_public_key(self):
public_key = self.cleaned_data['public_key']
if self.instance.public_key and public_key == self.instance.public_key:
msg = _('Public key should not be the same as your old one.')
raise forms.ValidationError(msg)
if public_key and not validate_ssh_public_key(public_key):
raise forms.ValidationError(_('Not a valid ssh public key'))
return public_key
def save(self):
public_key = self.cleaned_data['public_key']
if public_key:
self.instance.public_key = public_key
self.instance.save()
return self.instance
UserPublicKeyForm.verbose_name = _("Public key")
class FileForm(forms.Form):
file = forms.FileField()
# ~*~ coding: utf-8 ~*~
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.conf import settings
from common.utils import validate_ssh_public_key from common.utils import validate_ssh_public_key
from orgs.mixins.forms import OrgModelForm from orgs.mixins.forms import OrgModelForm
from .models import User, UserGroup from ..models import User
from .utils import check_password_rules, get_current_org_members from ..utils import (
check_password_rules, get_current_org_members, get_source_choices
)
class UserCheckPasswordForm(forms.Form): __all__ = [
username = forms.CharField(label=_('Username'), max_length=100) 'UserCreateForm', 'UserUpdateForm', 'UserBulkUpdateForm',
password = forms.CharField( 'UserCheckOtpCodeForm', 'UserCheckPasswordForm'
label=_('Password'), widget=forms.PasswordInput, ]
max_length=128, strip=False
)
class UserCheckOtpCodeForm(forms.Form):
otp_code = forms.CharField(label=_('MFA code'), max_length=6)
def get_source_choices():
choices_all = dict(User.SOURCE_CHOICES)
choices = [
(User.SOURCE_LOCAL, choices_all[User.SOURCE_LOCAL]),
]
if settings.AUTH_LDAP:
choices.append((User.SOURCE_LDAP, choices_all[User.SOURCE_LDAP]))
if settings.AUTH_OPENID:
choices.append((User.SOURCE_OPENID, choices_all[User.SOURCE_OPENID]))
if settings.AUTH_RADIUS:
choices.append((User.SOURCE_RADIUS, choices_all[User.SOURCE_RADIUS]))
return choices
class UserCreateUpdateFormMixin(OrgModelForm): class UserCreateUpdateFormMixin(OrgModelForm):
...@@ -157,131 +137,6 @@ class UserUpdateForm(UserCreateUpdateFormMixin): ...@@ -157,131 +137,6 @@ class UserUpdateForm(UserCreateUpdateFormMixin):
pass pass
class UserProfileForm(forms.ModelForm):
username = forms.CharField(disabled=True, label=_("Username"))
name = forms.CharField(disabled=True, label=_("Name"))
email = forms.CharField(disabled=True)
class Meta:
model = User
fields = [
'username', 'name', 'email',
'wechat', 'phone',
]
UserProfileForm.verbose_name = _("Profile")
class UserMFAForm(forms.ModelForm):
mfa_description = _(
'When enabled, '
'you will enter the MFA binding process the next time you log in. '
'you can also directly bind in '
'"personal information -> quick modification -> change MFA Settings"!')
class Meta:
model = User
fields = ['mfa_level']
widgets = {'mfa_level': forms.RadioSelect()}
help_texts = {
'mfa_level': _('* Enable MFA authentication '
'to make the account more secure.'),
}
UserMFAForm.verbose_name = _("MFA")
class UserFirstLoginFinishForm(forms.Form):
finish_description = _(
'In order to protect you and your company, '
'please keep your account, '
'password and key sensitive information properly. '
'(for example: setting complex password, enabling MFA authentication)'
)
UserFirstLoginFinishForm.verbose_name = _("Finish")
class UserPasswordForm(forms.Form):
old_password = forms.CharField(
max_length=128, widget=forms.PasswordInput,
label=_("Old password")
)
new_password = forms.CharField(
min_length=5, max_length=128,
widget=forms.PasswordInput,
label=_("New password")
)
confirm_password = forms.CharField(
min_length=5, max_length=128,
widget=forms.PasswordInput,
label=_("Confirm password")
)
def __init__(self, *args, **kwargs):
self.instance = kwargs.pop('instance')
super().__init__(*args, **kwargs)
def clean_old_password(self):
old_password = self.cleaned_data['old_password']
if not self.instance.check_password(old_password):
raise forms.ValidationError(_('Old password error'))
return old_password
def clean_confirm_password(self):
new_password = self.cleaned_data['new_password']
confirm_password = self.cleaned_data['confirm_password']
if new_password != confirm_password:
raise forms.ValidationError(_('Password does not match'))
return confirm_password
def save(self):
password = self.cleaned_data['new_password']
self.instance.reset_password(new_password=password)
return self.instance
class UserPublicKeyForm(forms.Form):
pubkey_description = _('Automatically configure and download the SSH key')
public_key = forms.CharField(
label=_('ssh public key'), max_length=5000, required=False,
widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}),
help_text=_('Paste your id_rsa.pub here.')
)
def __init__(self, *args, **kwargs):
if 'instance' in kwargs:
self.instance = kwargs.pop('instance')
else:
self.instance = None
super().__init__(*args, **kwargs)
def clean_public_key(self):
public_key = self.cleaned_data['public_key']
if self.instance.public_key and public_key == self.instance.public_key:
msg = _('Public key should not be the same as your old one.')
raise forms.ValidationError(msg)
if public_key and not validate_ssh_public_key(public_key):
raise forms.ValidationError(_('Not a valid ssh public key'))
return public_key
def save(self):
public_key = self.cleaned_data['public_key']
if public_key:
self.instance.public_key = public_key
self.instance.save()
return self.instance
UserPublicKeyForm.verbose_name = _("Public key")
class UserBulkUpdateForm(OrgModelForm): class UserBulkUpdateForm(OrgModelForm):
users = forms.ModelMultipleChoiceField( users = forms.ModelMultipleChoiceField(
required=True, required=True,
...@@ -333,40 +188,12 @@ class UserBulkUpdateForm(OrgModelForm): ...@@ -333,40 +188,12 @@ class UserBulkUpdateForm(OrgModelForm):
return users return users
class UserGroupForm(OrgModelForm): class UserCheckPasswordForm(forms.Form):
users = forms.ModelMultipleChoiceField( password = forms.CharField(
queryset=User.objects.none(), label=_('Password'), widget=forms.PasswordInput,
label=_("User"), max_length=128, strip=False
widget=forms.SelectMultiple(
attrs={
'class': 'users-select2',
'data-placeholder': _('Select users')
}
),
required=False,
) )
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.set_fields_queryset()
def set_fields_queryset(self): class UserCheckOtpCodeForm(forms.Form):
users_field = self.fields.get('users') otp_code = forms.CharField(label=_('MFA code'), max_length=6)
if self.instance:
users_field.initial = self.instance.users.all()
users_field.queryset = self.instance.users.all()
else:
users_field.queryset = User.objects.none()
def save(self, commit=True):
raise Exception("Save by restful api")
class Meta:
model = UserGroup
fields = [
'name', 'users', 'comment',
]
class FileForm(forms.Form):
file = forms.FileField()
...@@ -4,6 +4,7 @@ import uuid ...@@ -4,6 +4,7 @@ import uuid
from django.db import models, IntegrityError from django.db import models, IntegrityError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from common.utils import lazyproperty
from orgs.mixins.models import OrgModelMixin from orgs.mixins.models import OrgModelMixin
__all__ = ['UserGroup'] __all__ = ['UserGroup']
...@@ -20,6 +21,10 @@ class UserGroup(OrgModelMixin): ...@@ -20,6 +21,10 @@ class UserGroup(OrgModelMixin):
def __str__(self): def __str__(self):
return self.name return self.name
@lazyproperty
def users_amount(self):
return self.users.count()
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
unique_together = [('org_id', 'name'),] unique_together = [('org_id', 'name'),]
......
...@@ -19,6 +19,7 @@ from django.shortcuts import reverse ...@@ -19,6 +19,7 @@ from django.shortcuts import reverse
from orgs.utils import current_org from orgs.utils import current_org
from common.utils import get_signer, date_expired_default, get_logger, lazyproperty from common.utils import get_signer, date_expired_default, get_logger, lazyproperty
from common import fields from common import fields
from ..signals import post_user_change_password
__all__ = ['User'] __all__ = ['User']
...@@ -43,14 +44,10 @@ class AuthMixin: ...@@ -43,14 +44,10 @@ class AuthMixin:
self.set_password(password_raw_) self.set_password(password_raw_)
def set_password(self, raw_password): def set_password(self, raw_password):
self._set_password = True
if self.can_update_password(): if self.can_update_password():
self.date_password_last_updated = timezone.now() self.date_password_last_updated = timezone.now()
post_user_change_password.send(self.__class__, user=self)
super().set_password(raw_password) super().set_password(raw_password)
else:
error = _("User auth from {}, go there change password").format(
self.source)
raise PermissionError(error)
def can_update_password(self): def can_update_password(self):
return self.is_local return self.is_local
...@@ -196,22 +193,22 @@ class RoleMixin: ...@@ -196,22 +193,22 @@ class RoleMixin:
def is_app(self): def is_app(self):
return self.role == 'App' return self.role == 'App'
@property @lazyproperty
def user_orgs(self): def user_orgs(self):
from orgs.models import Organization from orgs.models import Organization
return Organization.get_user_user_orgs(self) return Organization.get_user_user_orgs(self)
@property @lazyproperty
def admin_orgs(self): def admin_orgs(self):
from orgs.models import Organization from orgs.models import Organization
return Organization.get_user_admin_orgs(self) return Organization.get_user_admin_orgs(self)
@property @lazyproperty
def audit_orgs(self): def audit_orgs(self):
from orgs.models import Organization from orgs.models import Organization
return Organization.get_user_audit_orgs(self) return Organization.get_user_audit_orgs(self)
@property @lazyproperty
def admin_or_audit_orgs(self): def admin_or_audit_orgs(self):
from orgs.models import Organization from orgs.models import Organization
return Organization.get_user_admin_or_audit_orgs(self) return Organization.get_user_admin_or_audit_orgs(self)
...@@ -223,26 +220,26 @@ class RoleMixin: ...@@ -223,26 +220,26 @@ class RoleMixin:
else: else:
return False return False
@property @lazyproperty
def is_org_auditor(self): def is_org_auditor(self):
if self.is_super_auditor or self.related_audit_orgs.exists(): if self.is_super_auditor or self.related_audit_orgs.exists():
return True return True
else: else:
return False return False
@property @lazyproperty
def can_admin_current_org(self): def can_admin_current_org(self):
return current_org.can_admin_by(self) return current_org.can_admin_by(self)
@property @lazyproperty
def can_audit_current_org(self): def can_audit_current_org(self):
return current_org.can_audit_by(self) return current_org.can_audit_by(self)
@property @lazyproperty
def can_user_current_org(self): def can_user_current_org(self):
return current_org.can_user_by(self) return current_org.can_user_by(self)
@property @lazyproperty
def can_admin_or_audit_current_org(self): def can_admin_or_audit_current_org(self):
return self.can_admin_current_org or self.can_audit_current_org return self.can_admin_current_org or self.can_audit_current_org
......
...@@ -4,29 +4,29 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -4,29 +4,29 @@ from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from common.fields import StringManyToManyField
from common.serializers import AdaptedBulkListSerializer from common.serializers import AdaptedBulkListSerializer
from orgs.mixins.serializers import BulkOrgResourceModelSerializer from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from django.db.models import Count
from ..models import User, UserGroup from ..models import User, UserGroup
from .. import utils from .. import utils
__all__ = [ __all__ = [
'UserGroupSerializer', 'UserGroupListSerializer', 'UserGroupSerializer',
'UserGroupUpdateMemberSerializer',
] ]
class UserGroupSerializer(BulkOrgResourceModelSerializer): class UserGroupSerializer(BulkOrgResourceModelSerializer):
users = serializers.PrimaryKeyRelatedField( users = serializers.PrimaryKeyRelatedField(
required=False, many=True, queryset=User.objects, label=_('User') required=False, many=True, queryset=User.objects, label=_('User'),
write_only=True
) )
class Meta: class Meta:
model = UserGroup model = UserGroup
list_serializer_class = AdaptedBulkListSerializer list_serializer_class = AdaptedBulkListSerializer
fields = [ fields = [
'id', 'name', 'users', 'comment', 'date_created', 'id', 'name', 'users', 'users_amount', 'comment',
'created_by', 'date_created', 'created_by',
] ]
extra_kwargs = { extra_kwargs = {
'created_by': {'label': _('Created by'), 'read_only': True} 'created_by': {'label': _('Created by'), 'read_only': True}
...@@ -47,23 +47,8 @@ class UserGroupSerializer(BulkOrgResourceModelSerializer): ...@@ -47,23 +47,8 @@ class UserGroupSerializer(BulkOrgResourceModelSerializer):
raise serializers.ValidationError(msg) raise serializers.ValidationError(msg)
return users return users
@classmethod
class UserGroupListSerializer(UserGroupSerializer): def setup_eager_loading(cls, queryset):
users = StringManyToManyField(many=True, read_only=True) """ Perform necessary eager loading of data. """
queryset = queryset.annotate(users_amount=Count('users'))
return queryset
class UserGroupUpdateMemberSerializer(serializers.ModelSerializer):
users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects)
class Meta:
model = UserGroup
fields = ['id', 'users']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_fields_queryset()
def set_fields_queryset(self):
users_field = self.fields['users']
users_field.child_relation.queryset = utils.get_current_org_members()
...@@ -7,11 +7,11 @@ from common.utils import validate_ssh_public_key ...@@ -7,11 +7,11 @@ from common.utils import validate_ssh_public_key
from common.mixins import BulkSerializerMixin from common.mixins import BulkSerializerMixin
from common.serializers import AdaptedBulkListSerializer from common.serializers import AdaptedBulkListSerializer
from common.permissions import CanUpdateDeleteUser from common.permissions import CanUpdateDeleteUser
from ..models import User, UserGroup from ..models import User
__all__ = [ __all__ = [
'UserSerializer', 'UserPKUpdateSerializer', 'UserUpdateGroupSerializer', 'UserSerializer', 'UserPKUpdateSerializer',
'ChangeUserPasswordSerializer', 'ResetOTPSerializer', 'ChangeUserPasswordSerializer', 'ResetOTPSerializer',
'UserProfileSerializer', 'UserDisplaySerializer', 'UserProfileSerializer', 'UserDisplaySerializer',
] ]
...@@ -123,16 +123,6 @@ class UserPKUpdateSerializer(serializers.ModelSerializer): ...@@ -123,16 +123,6 @@ class UserPKUpdateSerializer(serializers.ModelSerializer):
return value return value
class UserUpdateGroupSerializer(serializers.ModelSerializer):
groups = serializers.PrimaryKeyRelatedField(
many=True, queryset=UserGroup.objects
)
class Meta:
model = User
fields = ['id', 'groups']
class ChangeUserPasswordSerializer(serializers.ModelSerializer): class ChangeUserPasswordSerializer(serializers.ModelSerializer):
class Meta: class Meta:
......
...@@ -2,3 +2,4 @@ from django.dispatch import Signal ...@@ -2,3 +2,4 @@ from django.dispatch import Signal
post_user_create = Signal(providing_args=('user',)) post_user_create = Signal(providing_args=('user',))
post_user_change_password = Signal(providing_args=('user',))
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# #
from django.dispatch import receiver from django.dispatch import receiver
from django.db.models.signals import post_save, m2m_changed from django.db.models.signals import m2m_changed
from common.utils import get_logger from common.utils import get_logger
from .signals import post_user_create from .signals import post_user_create
......
{% extends '_without_nav_base.html' %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
<!DOCTYPE html> {% block body %}
<html> <article>
<head> <div class="" style="text-align: center; margin-bottom: 50px">
<meta charset="UTF-8"> <h2>
<title> {{ JMS_TITLE }} </title> {% block small_title %}
<link rel="shortcut icon" href="{{ FAVICON_URL }}" type="image/x-icon">
<link rel="stylesheet" href="{% static 'fonts/font_otp/iconfont.css' %}" />
<link rel="stylesheet" href="{% static 'css/otp.css' %}" />
<script src="{% static 'js/jquery-3.1.1.min.js' %}"></script>
<script src="{% static "js/plugins/qrcode/qrcode.min.js" %}"></script>
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
</head>
<body>
<!--头部-->
<header>
<div class="logo">
<a href="{% url 'index' %}">
<img src="{{ LOGO_URL }}" alt="" width="50px" height="50px"/>
</a>
<a href="{% url 'index' %}">{{ JMS_TITLE }}</a>
</div>
<div>
<a href="{% url 'index' %}">{% trans 'Home page' %}</a>
<b></b>
<a href="http://docs.jumpserver.org/zh/docs/">{% trans 'Docs' %}</a>
<b></b>
<a href="https://www.github.com/jumpserver/">GitHub</a>
</div>
</header>
<!--内容-->
<article>
<div class="" style="text-align: center; margin-bottom: 50px">
<h2>
{% block small_title %}
{% endblock %}
</h2>
</div>
<div >
<div class="verify">{% trans 'Security token validation' %}&nbsp;&nbsp;{% trans 'Account' %}&nbsp;<span>{{ user.username }}</span>&nbsp;&nbsp;{% trans 'Follow these steps to complete the binding operation' %}</div>
<hr style="width: 500px; margin: auto; margin-top: 10px;">
{% block content %}
{% endblock %} {% endblock %}
</div> </h2>
</article> </div>
<div >
<div class="verify">{% trans 'Security token validation' %}&nbsp;&nbsp;{% trans 'Account' %}&nbsp;<span>{{ user.username }}</span>&nbsp;&nbsp;{% trans 'Follow these steps to complete the binding operation' %}</div>
<footer> <hr style="width: 500px; margin: auto; margin-top: 10px;">
<div class="" style="margin-top: 100px;"> {% block content %}
{% include '_copyright.html' %} {% endblock %}
</div> </div>
</footer> </article>
{% endblock %}
</body>
</html>
{% 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 '_import_modal.html' %}
{% load i18n %}
{% block modal_title%}{% trans "Import users" %}{% endblock %}
{% block import_modal_download_template_url %}{% url "api-users:user-list" %}{% endblock %}
{% extends '_update_modal.html' %}
{% load i18n %}
{% block modal_title%}{% trans "Update user" %}{% endblock %}
\ No newline at end of file
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
{% block content %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-sm-12">
<div class="ibox"> <div class="ibox">
<div class="ibox-title"> <div class="ibox-title">
<h5>{% trans 'First Login' %}</h5> <h5>{% trans 'First Login' %}</h5>
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
</div> </div>
<div class="content clearfix" style="background-color: #eee; border-radius:5px;"> <div class="content clearfix" style="background-color: #eee; border-radius:5px;">
<div class="row"> <div class="row">
<form action="" method="post" class="form col-lg-8 p-m" id="fl_form" style="padding-left: 40px;"> <form action="" method="post" class="form col-sm-8 p-m" id="fl_form" style="padding-left: 40px;">
{% csrf_token %} {% csrf_token %}
{{ wizard.management_form }} {{ wizard.management_form }}
{#{% if wizard.form.forms %}#} {#{% if wizard.form.forms %}#}
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
{% endif %} {% endif %}
</form> </form>
<div class="col-lg-4"> <div class="col-sm-4">
<div class="text-center"> <div class="text-center">
<div style="margin-top: 20px"> <div style="margin-top: 20px">
<i class="fa fa-sign-in" style="font-size: 180px;color: #e5e5e5 "></i> <i class="fa fa-sign-in" style="font-size: 180px;color: #e5e5e5 "></i>
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
{% block content %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-sm-12">
<div class="ibox"> <div class="ibox">
<div class="ibox-title"> <div class="ibox-title">
<h5>{% trans 'First Login' %}</h5> <h5>{% trans 'First Login' %}</h5>
......
{% extends '_base_only_content.html' %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
<!DOCTYPE html> {% load bootstrap3 %}
<html> {% block custom_head_css_js %}
<style>
<head> .captcha {
<meta charset="utf-8"> float: right;
<meta name="viewport" content="width=device-width, initial-scale=1.0"> }
<link rel="shortcut icon" href="{{ FAVICON_URL }}" type="image/x-icon"> </style>
<title>{% trans 'Forgot password' %}</title> {% endblock %}
{% block html_title %}{% trans 'Forgot password' %}{% endblock %}
{% include '_head_css_js.html' %} {% block title %} {% trans 'Forgot password' %}?{% endblock %}
<link href="{% static "css/jumpserver.css" %}" rel="stylesheet">
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script> {% block content %}
<script src="{% static "js/jumpserver.js" %}"></script> {% if errors %}
</head> <p class="red-fonts">{{ errors }}</p>
{% endif %}
<body class="gray-bg"> <p>
<div class="passwordBox animated fadeInDown"> {% trans 'Input your email, that will send a mail to your' %}
<div class="row"> </p>
<div class="col-md-12"> <div class="row">
<div class="ibox-content"> <div class="col-sm-12">
<img src="{{ LOGO_URL }}" style="margin: auto" width="82" height="82"> <form role="form" class="form-horizontal" action="" method="post">
<h2 class="font-bold" style="display: inline">{% trans 'Forgot password' %} ?</h2> {% csrf_token %}
<h1></h1> {% bootstrap_field form.email layout="horizontal" %}
{% if errors %} {% bootstrap_field form.captcha layout="horizontal" %}
<p class="red-fonts">{{ errors }}</p> <button type="submit" class="btn btn-primary block full-width m-b">{% trans 'Submit' %}</button>
{% endif %} </form>
<p>
{% trans 'Input your email, that will send a mail to your' %}
</p>
<div class="row">
<div class="col-lg-12">
<form class="m-t" role="form" action="" method="post">
{% csrf_token %}
<div class="form-group">
<input type="email" name="email" class="form-control" placeholder="Email address" required="">
</div>
<button type="submit" class="btn btn-primary block full-width m-b">{% trans 'Submit' %}</button>
</form>
</div>
</div>
</div>
</div>
</div>
<hr/>
<div class="row">
<div class="col-md-12">
{% include '_copyright.html' %}
</div>
</div> </div>
</div> </div>
{% endblock %}
</body>
</html>
{% extends '_base_only_content.html' %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
<!DOCTYPE html> {% load bootstrap3 %}
<html> {% block html_title %}{% trans 'Reset password' %}{% endblock %}
{% block title %}{% trans 'Reset password' %}{% endblock %}
<head>
<meta charset="utf-8"> {% block content %}
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <form class="m-t" role="form" method="post" action="">
<title> {{ JMS_TITLE }} </title> {% csrf_token %}
<link rel="shortcut icon" href="{{ FAVICON_URL }}" type="image/x-icon"> {% if errors %}
{% include '_head_css_js.html' %} <p class="red-fonts">{{ errors }}</p>
<link href="{% static "css/jumpserver.css" %}" rel="stylesheet"> {% endif %}
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script> {% if not token_invalid %}
<script src="{% static "js/jumpserver.js" %}"></script> <div class="form-group">
<script type="text/javascript" src="{% static 'js/pwstrength-bootstrap.js' %}"></script> {% bootstrap_field form.new_password %}
{% bootstrap_field form.confirm_password %}
</head> {# 密码popover #}
<div id="container">
<body class="gray-bg"> <div class="popover fade bottom in" role="tooltip" id="popover777" style=" display: none; width:260px;">
<div class="arrow" style="left: 50%;"></div>
<div class="loginColumns animated fadeInDown"> <h3 class="popover-title" style="display: none;"></h3>
<div class="row"> <h4>{% trans 'Your password must satisfy' %}</h4><div id="id_password_rules" style="color: #908a8a; margin-left:20px; font-size:15px;"></div>
<h4 style="margin-top: 10px;">{% trans 'Password strength' %}</h4><div id="id_progress"></div>
<div class="col-md-6"> <div class="popover-content"></div>
<h2 class="font-bold">{% trans 'Welcome to the Jumpserver open source fortress' %}</h2>
<p>
{% trans 'Jumpserver is an open source desktop system developed using Python and Django that helps Internet businesses with efficient users, assets, permissions, and audit management' %}
</p>
<p>
{% trans 'We are from all over the world, we have great admiration and worship for the spirit of open source, we have endless pursuit for perfection, neatness and elegance' %}
</p>
<p>
{% trans 'We focus on automatic operation and maintenance, and strive to build an easy-to-use, stable, safe and automatic board hopping machine, which is our unremitting pursuit and power' %}
</p>
<p>
<small>{% trans 'Always young, always with tears in my eyes. Stay foolish Stay hungry' %}</small>
</p>
</div>
<div class="col-md-6">
<div class="ibox-content">
<div><img src="{{ LOGO_URL }}" width="82" height="82"> <span class="font-bold text-center" style="font-size: 32px; font-family: inherit">{% trans 'Reset password' %}</span></div>
<form class="m-t" role="form" method="post" action="">
{% csrf_token %}
{% if errors %}
<p class="red-fonts">{{ errors }}</p>
{% endif %}
<div class="form-group">
<input type="password" id="id_new_password" class="form-control" name="password" placeholder="{% trans 'Password' %}" required="">
{# 密码popover #}
<div id="container">
<div class="popover fade bottom in" role="tooltip" id="popover777" style=" display: none; width:260px;">
<div class="arrow" style="left: 50%;"></div>
<h3 class="popover-title" style="display: none;"></h3>
<h4>{% trans 'Your password must satisfy' %}</h4><div id="id_password_rules" style="color: #908a8a; margin-left:20px; font-size:15px;"></div>
<h4 style="margin-top: 10px;">{% trans 'Password strength' %}</h4><div id="id_progress"></div>
<div class="popover-content"></div>
</div>
</div>
</div>
<div class="form-group">
<input type="password" id="id_confirm_password" class="form-control" name="password-confirm" placeholder="{% trans 'Password again' %}" required="">
</div>
<button type="submit" class="btn btn-primary block full-width m-b">{% trans "Setting" %}</button>
<a href="#">
<small>Forgot password?</small>
</a>
<p class="text-muted text-center">
</p>
</form>
<p class="m-t">
</p>
</div> </div>
</div> </div>
</div> </div>
<hr/> <button type="submit" class="btn btn-primary block full-width m-b">{% trans "Setting" %}</button>
<div class="row"> {% endif %}
<div class="col-md-12"> </form>
{% include '_copyright.html' %} {% endblock %}
</div>
</div> {% block custom_foot_js %}
</div> <script type="text/javascript" src="{% static 'js/pwstrength-bootstrap.js' %}"></script>
<script>
</body> $(document).ready(function () {
// 密码强度校验
</html> var el = $('#id_password_rules'),
<script> idPassword = $('#id_new_password'),
$(document).ready(function () { idPopover = $('#popover777'),
// 密码强度校验 container = $('#container'),
var el = $('#id_password_rules'), progress = $('#id_progress'),
idPassword = $('#id_new_password'), password_check_rules = {{ password_check_rules|safe }},
idPopover = $('#popover777'), minLength = 6,
container = $('#container'), top = 146, left = 170,
progress = $('#id_progress'), i18n_fallback = {
password_check_rules = {{ password_check_rules|safe }}, "veryWeak": "{% trans 'Very weak' %}",
minLength = 6, "weak": "{% trans 'Weak' %}",
top = 146, left = 170, "normal": "{% trans 'Normal' %}",
i18n_fallback = { "medium": "{% trans 'Medium' %}",
"veryWeak": "{% trans 'Very weak' %}", "strong": "{% trans 'Strong' %}",
"weak": "{% trans 'Weak' %}", "veryStrong": "{% trans 'Very strong' %}"
"normal": "{% trans 'Normal' %}", };
"medium": "{% trans 'Medium' %}",
"strong": "{% trans 'Strong' %}", jQuery.each(password_check_rules, function (idx, rules) {
"veryStrong": "{% trans 'Very strong' %}" if(rules.key === 'id_security_password_min_length'){
}; minLength = rules.value
}
jQuery.each(password_check_rules, function (idx, rules) { });
if(rules.key === 'id_security_password_min_length'){
minLength = rules.value // 初始化popover
} initPopover(container, progress, idPassword, el, password_check_rules, i18n_fallback);
});
// 监听事件
// 初始化popover idPassword.on('focus', function () {
initPopover(container, progress, idPassword, el, password_check_rules, i18n_fallback); idPopover.css('top', top);
idPopover.css('left', left);
// 监听事件 idPopover.css('display', 'block');
idPassword.on('focus', function () { });
idPopover.css('top', top); idPassword.on('blur', function () {
idPopover.css('left', left); idPopover.css('display', 'none');
idPopover.css('display', 'block'); });
}); idPassword.on('keyup', function(){
idPassword.on('blur', function () { var password = idPassword.val();
idPopover.css('display', 'none'); checkPasswordRules(password, minLength);
}); })
idPassword.on('keyup', function(){ })
var password = idPassword.val();
checkPasswordRules(password, minLength);
})
})
</script> </script>
{% endblock %}
...@@ -244,10 +244,10 @@ ...@@ -244,10 +244,10 @@
{% for group in user_object.groups.all %} {% for group in user_object.groups.all %}
<tr> <tr>
<td > <td >
<b class="bdg_group" data-gid={{ group.id }}>{{ group.name }}</b> <b class="bdg_group" >{{ group.name }}</b>
</td> </td>
<td> <td>
<button class="btn btn-danger pull-right btn-xs btn_leave_group" type="button"><i class="fa fa-minus"></i></button> <button class="btn btn-danger pull-right btn-xs btn_leave_group" data-uid={{ group.id }} type="button"><i class="fa fa-minus"></i></button>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
...@@ -307,38 +307,8 @@ ...@@ -307,38 +307,8 @@
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
jumpserver.nodes_selected = {};
var usersSelect2; var usersSelect2;
function updateUserGroups(groups) {
var the_url = "{% url 'api-users:user-update-group' pk=user_object.id %}";
var body = {
groups: Object.assign([], groups)
};
var success = function(data) {
// remove all the selected groups from select > option and rendered ul element;
$('.select2-selection__rendered').empty();
$('#groups_selected').val('');
$.map(jumpserver.nodes_selected, function(group_name, index) {
$('#opt_' + index).remove();
// change tr html of user groups.
$('.group_edit tbody').append(
'<tr>' +
'<td><b class="bdg_group" data-gid="' + index + '">' + group_name + '</b></td>' +
'<td><button class="btn btn-danger btn-xs pull-right btn_leave_group" type="button"><i class="fa fa-minus"></i></button></td>' +
'</tr>'
)
});
// clear jumpserver.groups_selected
jumpserver.nodes_selected = {};
};
requestApi({
url: the_url,
body: JSON.stringify(body),
success: success
});
}
function updateUserLoginReviewer(reviewers) { function updateUserLoginReviewer(reviewers) {
var url = "{% url 'api-auth:login-confirm-setting-update' user_id=user_object.id %}"; var url = "{% url 'api-auth:login-confirm-setting-update' user_id=user_object.id %}";
var data = {reviewers: reviewers}; var data = {reviewers: reviewers};
...@@ -352,16 +322,44 @@ function updateUserLoginReviewer(reviewers) { ...@@ -352,16 +322,44 @@ function updateUserLoginReviewer(reviewers) {
}) })
} }
var usersGroupsRelationUrl = "{% url 'api-users:users-groups-relation-list' %}";
function addObjects(objectsId) {
if (!objectsId || objectsId.length === 0) {
return
}
var theUrl = usersGroupsRelationUrl;
var body = [];
objectsId.forEach(function (v) {
var data = {user: "{{ object.id }}"};
data["usergroup"] = v;
body.push(data)
});
requestApi({
url: theUrl,
body: JSON.stringify(body),
method: "POST",
success: reloadPage
});
}
function removeObject(objectId) {
if (!objectId) {
return
}
var theUrl = usersGroupsRelationUrl;
theUrl = setUrlParam(theUrl, 'user', "{{ object.id }}");
theUrl = setUrlParam(theUrl, 'usergroup', objectId);
requestApi({
url: theUrl,
method: "DELETE",
success: reloadPage
});
}
$(document).ready(function() { $(document).ready(function() {
$('.select2').select2() $('.select2').select2();
.on('select2:select', function(evt) {
var data = evt.params.data;
jumpserver.nodes_selected[data.id] = data.text;
})
.on('select2:unselect', function(evt) {
var data = evt.params.data;
delete jumpserver.nodes_selected[data.id];
});
usersSelect2 = usersSelect2Init('.users-select2') usersSelect2 = usersSelect2Init('.users-select2')
}) })
.on('click', '#is_active', function() { .on('click', '#is_active', function() {
...@@ -405,31 +403,11 @@ $(document).ready(function() { ...@@ -405,31 +403,11 @@ $(document).ready(function() {
}); });
}) })
.on('click', '#btn_join_group', function() { .on('click', '#btn_join_group', function() {
if (Object.keys(jumpserver.nodes_selected).length === 0) { var objectsId = $("#groups_selected").val();
return false; addObjects(objectsId);
}
var groups = $('.bdg_group').map(function() {
return $(this).data('gid');
}).get();
$.map(jumpserver.nodes_selected, function(value, index) {
groups.push(index);
$('#opt_' + index).remove();
});
updateUserGroups(groups)
}).on('click', '.btn_leave_group', function() { }).on('click', '.btn_leave_group', function() {
var $this = $(this); var objectId = $(this).data('uid');
var $tr = $this.closest('tr'); removeObject(objectId)
var $badge = $tr.find('.bdg_group');
var gid = $badge.data('gid');
var group_name = $badge.html() || $badge.text();
$('#groups_selected').append(
'<option value="' + gid + '" id="opt_' + gid + '">' + group_name + '</option>'
);
$tr.remove();
var groups = $('.bdg_group').map(function() {
return $(this).data('gid');
}).get();
updateUserGroups(groups)
}).on('click', '#btn-reset-password', function() { }).on('click', '#btn-reset-password', function() {
function doReset() { function doReset() {
var the_url = '{% url "api-users:user-reset-password" pk=user_object.id %}'; var the_url = '{% url "api-users:user-reset-password" pk=user_object.id %}';
......
...@@ -91,9 +91,9 @@ ...@@ -91,9 +91,9 @@
{% for user in user_group.users.all %} {% for user in user_group.users.all %}
<tr> <tr>
<td ><b class="bdg_user" data-uid={{ user.id }}>{{ user.name }}</b></td> <td ><b class="bdg_user" >{{ user.name }}</b></td>
<td> <td>
<button class="btn btn-danger pull-right btn-xs btn-remove-user" type="button"><i class="fa fa-minus"></i></button> <button class="btn btn-danger pull-right btn-xs btn-remove-user" data-uid={{ user.id }} type="button"><i class="fa fa-minus"></i></button>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
...@@ -110,73 +110,51 @@ ...@@ -110,73 +110,51 @@
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
jumpserver.users_selected = {};
function updateGroupMember(users) { var usersGroupsRelationUrl = "{% url 'api-users:users-groups-relation-list' %}";
var the_url = "{% url 'api-users:user-group-update-user' pk=user_group.id %}";
var body = { function addObjects(objectsId) {
users: Object.assign([], users) if (!objectsId || objectsId.length === 0) {
}; return
var success = function(data) { }
// remove all the selected groups from select > option and rendered ul element; var theUrl = usersGroupsRelationUrl;
$('.select2-selection__rendered').empty(); var body = [];
$('#slct_users').val(''); objectsId.forEach(function (v) {
$.map(jumpserver.users_selected, function(user_name, index) { var data = {usergroup: "{{ object.id }}"};
$('#opt_' + index).remove(); data["user"] = v;
// change tr html of users body.push(data)
$('.user_edit tbody').append( });
'<tr>' +
'<td><b class="bdg_user" data-uid="' + index + '">' + user_name + '</b></td>' +
'<td><button class="btn btn-danger btn-xs pull-right btn-remove-user" type="button"><i class="fa fa-minus"></i></button></td>' +
'</tr>'
)
});
// clear jumpserver.selected_groups
jumpserver.users_selected = {};
};
requestApi({ requestApi({
url: the_url, url: theUrl,
body: JSON.stringify(body), body: JSON.stringify(body),
success: success method: "POST",
success: reloadPage
});
}
function removeObject(objectId, type) {
if (!objectId) {
return
}
var theUrl = usersGroupsRelationUrl;
theUrl = setUrlParam(theUrl, 'usergroup', "{{ object.id }}");
theUrl = setUrlParam(theUrl, 'user', objectId);
requestApi({
url: theUrl,
method: "DELETE",
success: reloadPage
}); });
} }
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2() $('.select2').select2();
.on('select2:select', function(evt) {
var data = evt.params.data;
jumpserver.users_selected[data.id] = data.text;
}).on('select2:unselect', function(evt) {
var data = evt.params.data;
delete jumpserver.users_selected[data.id]
});
usersSelect2Init('#slct_users') usersSelect2Init('#slct_users')
}).on('click', '.btn-remove-user', function() { }).on('click', '.btn-remove-user', function() {
var $this = $(this); var objectId = $(this).data("uid");
var $tr = $this.closest('tr'); removeObject(objectId);
var $badge = $tr.find('.bdg_user');
var uid = $badge.data('uid');
var user_name = $badge.html() || $badge.text();
$('#slct_users').append(
'<option value="' + uid + '" id="opt_' + uid + '">' + user_name + '</option>'
);
$tr.remove();
var users = $('.bdg_user').map(function() {
return $(this).data('uid');
}).get();
updateGroupMember(users)
}).on('click', '#btn_add_user', function() { }).on('click', '#btn_add_user', function() {
if (Object.keys(jumpserver.users_selected).length === 0) { var objectsId = $("#slct_users").val();
return false; addObjects(objectsId);
}
var users = $('.bdg_user').map(function() {
return $(this).data('uid');
}).get();
$.map(jumpserver.users_selected, function(value, index) {
users.push(index);
$('#opt_' + index).remove();
});
updateGroupMember(users)
}).on('click', '.btn-delete-user-group', function () { }).on('click', '.btn-delete-user-group', function () {
var $this = $(this); var $this = $(this);
var name = "{{ user_group.name }}"; var name = "{{ user_group.name }}";
...@@ -185,5 +163,6 @@ $(document).ready(function () { ...@@ -185,5 +163,6 @@ $(document).ready(function () {
var redirect_url = "{% url 'users:user-group-list' %}"; var redirect_url = "{% url 'users:user-group-list' %}";
objectDelete($this, name, the_url, redirect_url); objectDelete($this, name, the_url, redirect_url);
}) })
</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="" style="float: right"> {% include '_csv_import_export.html' %}
<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 %}
<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>
...@@ -39,14 +18,13 @@ ...@@ -39,14 +18,13 @@
</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 %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
var groups_table = 0; var groupsTable = 0;
var usersAmountTpl = '<a class="group-users-amount" data-uid="ID">NUM</a>';
function initTable() { function initTable() {
var options = { var options = {
ele: $('#group_list_table'), ele: $('#group_list_table'),
...@@ -58,14 +36,16 @@ function initTable() { ...@@ -58,14 +36,16 @@ function initTable() {
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}}, }},
{targets: 2, createdCell: function (td, cellData, rowData) { {targets: 2, createdCell: function (td, cellData, rowData) {
var html = createPopover(cellData); var data = usersAmountTpl
$(td).html(html); .replace("ID", rowData.id)
.replace('NUM', cellData);
$(td).html(data);
}}, }},
{targets: 3, createdCell: function (td, cellData) { {targets: 3, createdCell: function (td, cellData) {
cellData = htmlEscape(cellData); cellData = htmlEscape(cellData);
var innerHtml = cellData.length > 30 ? cellData.substring(0, 30) + '...': cellData; var innerHtml = cellData.length > 30 ? cellData.substring(0, 30) + '...': cellData;
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>'); $(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>');
}}, }},
{targets: 4, createdCell: function (td, cellData, rowData) { {targets: 4, createdCell: function (td, cellData, rowData) {
var name = htmlEscape(rowData.name); var name = htmlEscape(rowData.name);
var update_btn = '<a href="{% url "users:user-group-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>' var update_btn = '<a href="{% url "users:user-group-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'
...@@ -80,129 +60,53 @@ function initTable() { ...@@ -80,129 +60,53 @@ function initTable() {
} }
}} }}
], ],
ajax_url: '{% url "api-users:user-group-list" %}?display=1', ajax_url: '{% url "api-users:user-group-list" %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "users", orderable: false}, columns: [{data: "id"}, {data: "name" }, {data: "users_amount", orderable: false},
{data: "comment"}, {data: "id", orderable: false, width:"100px"}], {data: "comment"}, {data: "id", orderable: false, width:"100px"}],
order: [],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
groups_table = jumpserver.initServerSideDataTable(options); groupsTable = jumpserver.initServerSideDataTable(options);
return groups_table return groupsTable
} }
$(document).ready(function() {
initTable()
var usersGroupsRelationUrl = "{% url 'api-users:users-groups-relation-list' %}";
function getGroupUsers(groupId, callback) {
var theUrl = setUrlParam(usersGroupsRelationUrl, "usergroup", groupId);
if (!callback) {
callback = function (data) {
console.log(data)
}
}
requestApi({
url: theUrl,
method: "GET",
success: callback,
flash_message: false,
})
}
$(document).ready(function() {
groupsTable = initTable();
initCsvImportExport(groupsTable, "{% trans 'User groups' %}")
}).on('click', '.btn_delete_user_group', function(){ }).on('click', '.btn_delete_user_group', function(){
var $this = $(this); var $this = $(this);
var group_id = $this.data('gid'); var group_id = $this.data('gid');
var name = $this.data('name'); var name = $this.data('name');
var the_url = "{% url 'api-users:user-group-detail' pk=DEFAULT_PK %}".replace('{{ DEFAULT_PK }}', group_id); var the_url = "{% url 'api-users:user-group-detail' pk=DEFAULT_PK %}".replace('{{ DEFAULT_PK }}', group_id);
objectDelete($this, name, the_url) objectDelete($this, name, the_url)
}).on('click', '#btn_bulk_update', function(){
var action = $('#slct_bulk_update').val();
var $data_table = $('#group_list_table').DataTable()
var plain_id_list = [];
$data_table.rows({selected: true}).every(function(){
plain_id_list.push(this.data().id);
});
if (plain_id_list === []) {
return false;
}
var the_url = "{% url 'api-users:user-group-list' %}";
function doDelete() {
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected groups !!!' %}",
type: "warning",
showCancelButton: true,
cancelButtonText: "{% trans 'Cancel' %}",
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
var success = function() {
var msg = "{% trans 'UserGroups Deleted.' %}";
swal("{% trans 'UserGroups Delete' %}", msg, "success");
$data_table.ajax.reload();
};
var fail = function() {
var msg = "{% trans 'UserGroup Deleting failed.' %}";
swal("{% trans 'UserGroups Delete' %}", msg, "error");
};
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
requestApi({url: url_delete, method: 'DELETE', success: success, error: fail});
jumpserver.checked = false;
});
}
switch(action) {
case 'delete':
doDelete();
break;
default:
break;
}
}).on('click', '.btn_export', function(){
var groups = groups_table.selected;
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(){ .on('click', '.group-users-amount', function () {
var groups = groups_table.selected; var $this = $(this);
var data = { var groupId = $(this).data("uid");
'resources': groups getGroupUsers(groupId, function (data) {
}; var groups = [];
var search = $("input[type='search']").val(); data.forEach(function (v) {
var props = { groups.push(v.user_display);
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
}); });
}) var popover = createPopover(groups);
$this.parent().html(popover);
$(popover).trigger('click');
})
});
</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="pull-right" > {% include '_csv_import_export.html' %}
<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 %}
<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>
...@@ -63,14 +42,12 @@ ...@@ -63,14 +42,12 @@
</div> </div>
</div> </div>
</div> </div>
{% 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 %}
<script src="{% static 'js/jquery.form.min.js' %}"></script> <script src="{% static 'js/jquery.form.min.js' %}"></script>
<script> <script>
var users_table = 0; var usersTable = 0;
function initTable() { function initTable() {
var options = { var options = {
ele: $('#user_list_table'), ele: $('#user_list_table'),
...@@ -148,86 +125,17 @@ function initTable() { ...@@ -148,86 +125,17 @@ function initTable() {
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
users_table = jumpserver.initServerSideDataTable(options); usersTable = jumpserver.initServerSideDataTable(options);
return users_table return usersTable
} }
$(document).ready(function(){ $(document).ready(function(){
initTable(); usersTable = initTable();
var fields = $('#fm_user_bulk_update .form-group'); initCsvImportExport(usersTable, "{% trans 'User groups' %}")
$.each(fields, function (index, value) {
console.log(value)
});
$('.btn_export').click(function () {
var users = users_table.selected;
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',
params: {
search: search
}
};
APIExportData(props);
});
$('#btn_import_confirm').click(function() {
var url = "{% url 'api-users:user-list' %}";
var file = document.getElementById('id_file').files[0];
if(!file){
toastr.error("{% trans "Please select file" %}");
return
}
var data_table = $('#user_list_table').DataTable();
APIImportData({
url: url,
method: "POST",
body: file,
data_table: data_table
});
});
$('#download_update_template').click(function () {
var users = users_table.selected;
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
}
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 id_list = users_table.selected; var id_list = usersTable.selected;
if (id_list.length === 0) { if (id_list.length === 0) {
return false; return false;
} }
......
...@@ -9,11 +9,6 @@ ...@@ -9,11 +9,6 @@
{% block content %} {% block content %}
<form class="" role="form" method="post" action=""> <form class="" role="form" method="post" action="">
{% csrf_token %} {% csrf_token %}
<div class="form-input">
<input type="text" class="" name="{{ form.username.html_name }}" value="{{ form.username.value }}" readonly="readonly" required="">
</div>
<div class="form-input"> <div class="form-input">
<input type="password" class="" name="{{ form.password.html_name }}" placeholder="{% trans 'Password' %}" required=""> <input type="password" class="" name="{{ form.password.html_name }}" placeholder="{% trans 'Password' %}" required="">
</div> </div>
......
...@@ -14,7 +14,7 @@ app_name = 'users' ...@@ -14,7 +14,7 @@ app_name = 'users'
router = BulkRouter() router = BulkRouter()
router.register(r'users', api.UserViewSet, 'user') router.register(r'users', api.UserViewSet, 'user')
router.register(r'groups', api.UserGroupViewSet, 'user-group') router.register(r'groups', api.UserGroupViewSet, 'user-group')
router.register(r'users-groups-relations', api.UserUserGroupRelationViewSet, 'user-group-relation') router.register(r'users-groups-relations', api.UserUserGroupRelationViewSet, 'users-groups-relation')
urlpatterns = [ urlpatterns = [
...@@ -28,8 +28,6 @@ urlpatterns = [ ...@@ -28,8 +28,6 @@ urlpatterns = [
path('users/<uuid:pk>/pubkey/reset/', api.UserResetPKApi.as_view(), name='user-public-key-reset'), path('users/<uuid:pk>/pubkey/reset/', api.UserResetPKApi.as_view(), name='user-public-key-reset'),
path('users/<uuid:pk>/pubkey/update/', api.UserUpdatePKApi.as_view(), name='user-public-key-update'), path('users/<uuid:pk>/pubkey/update/', api.UserUpdatePKApi.as_view(), name='user-public-key-update'),
path('users/<uuid:pk>/unblock/', api.UserUnblockPKApi.as_view(), name='user-unblock'), path('users/<uuid:pk>/unblock/', api.UserUnblockPKApi.as_view(), name='user-unblock'),
path('users/<uuid:pk>/groups/', api.UserUpdateGroupApi.as_view(), name='user-update-group'),
path('groups/<uuid:pk>/users/', api.UserGroupUpdateUserApi.as_view(), name='user-group-update-user'),
] ]
urlpatterns += router.urls urlpatterns += router.urls
......
...@@ -20,10 +20,10 @@ urlpatterns = [ ...@@ -20,10 +20,10 @@ urlpatterns = [
path('profile/password/update/', views.UserPasswordUpdateView.as_view(), name='user-password-update'), path('profile/password/update/', views.UserPasswordUpdateView.as_view(), name='user-password-update'),
path('profile/pubkey/update/', views.UserPublicKeyUpdateView.as_view(), name='user-pubkey-update'), path('profile/pubkey/update/', views.UserPublicKeyUpdateView.as_view(), name='user-pubkey-update'),
path('profile/pubkey/generate/', views.UserPublicKeyGenerateView.as_view(), name='user-pubkey-generate'), path('profile/pubkey/generate/', views.UserPublicKeyGenerateView.as_view(), name='user-pubkey-generate'),
path('profile/otp/enable/authentication/', views.UserOtpEnableAuthenticationView.as_view(), name='user-otp-enable-authentication'), path('profile/otp/enable/authentication/', views.UserCheckPasswordView.as_view(), name='user-otp-enable-authentication'),
path('profile/otp/enable/install-app/', views.UserOtpEnableInstallAppView.as_view(), name='user-otp-enable-install-app'), path('profile/otp/enable/install-app/', views.UserOtpEnableInstallAppView.as_view(), name='user-otp-enable-install-app'),
path('profile/otp/enable/bind/', views.UserOtpEnableBindView.as_view(), name='user-otp-enable-bind'), path('profile/otp/enable/bind/', views.UserOtpEnableBindView.as_view(), name='user-otp-enable-bind'),
path('profile/otp/disable/authentication/', views.UserOtpDisableAuthenticationView.as_view(), name='user-otp-disable-authentication'), path('profile/otp/disable/authentication/', views.UserDisableMFAView.as_view(), name='user-otp-disable-authentication'),
path('profile/otp/update/', views.UserOtpUpdateView.as_view(), name='user-otp-update'), path('profile/otp/update/', views.UserOtpUpdateView.as_view(), name='user-otp-update'),
path('profile/otp/settings-success/', views.UserOtpSettingsSuccessView.as_view(), name='user-otp-settings-success'), path('profile/otp/settings-success/', views.UserOtpSettingsSuccessView.as_view(), name='user-otp-settings-success'),
......
...@@ -329,3 +329,18 @@ def construct_user_email(username, email): ...@@ -329,3 +329,18 @@ def construct_user_email(username, email):
def get_current_org_members(exclude=()): def get_current_org_members(exclude=()):
from orgs.utils import current_org from orgs.utils import current_org
return current_org.get_org_members(exclude=exclude) return current_org.get_org_members(exclude=exclude)
def get_source_choices():
from .models import User
choices_all = dict(User.SOURCE_CHOICES)
choices = [
(User.SOURCE_LOCAL, choices_all[User.SOURCE_LOCAL]),
]
if settings.AUTH_LDAP:
choices.append((User.SOURCE_LDAP, choices_all[User.SOURCE_LDAP]))
if settings.AUTH_OPENID:
choices.append((User.SOURCE_OPENID, choices_all[User.SOURCE_OPENID]))
if settings.AUTH_RADIUS:
choices.append((User.SOURCE_RADIUS, choices_all[User.SOURCE_RADIUS]))
return choices
...@@ -4,13 +4,13 @@ from __future__ import unicode_literals ...@@ -4,13 +4,13 @@ from __future__ import unicode_literals
from django.shortcuts import render from django.shortcuts import render
from django.views.generic import RedirectView from django.views.generic import RedirectView
from django.core.files.storage import default_storage from django.core.files.storage import default_storage
from django.http import HttpResponseRedirect
from django.shortcuts import reverse, redirect from django.shortcuts import reverse, redirect
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.conf import settings from django.conf import settings
from django.urls import reverse_lazy from django.urls import reverse_lazy
from formtools.wizard.views import SessionWizardView from formtools.wizard.views import SessionWizardView
from django.views.generic import FormView
from common.utils import get_object_or_none from common.utils import get_object_or_none
from common.permissions import PermissionsMixin, IsValidUser from common.permissions import PermissionsMixin, IsValidUser
...@@ -33,22 +33,24 @@ class UserLoginView(RedirectView): ...@@ -33,22 +33,24 @@ class UserLoginView(RedirectView):
query_string = True query_string = True
class UserForgotPasswordView(TemplateView): class UserForgotPasswordView(FormView):
template_name = 'users/forgot_password.html' template_name = 'users/forgot_password.html'
form_class = forms.UserForgotPasswordForm
def post(self, request): def form_valid(self, form):
email = request.POST.get('email') request = self.request
email = form.cleaned_data['email']
user = get_object_or_none(User, email=email) user = get_object_or_none(User, email=email)
if not user: if not user:
error = _('Email address invalid, please input again') error = _('Email address invalid, please input again')
return self.get(request, errors=error) return self.get(request, errors=error)
elif not user.can_update_password(): elif not user.can_update_password():
error = _('User auth from {}, go there change password'.format(user.source)) error = _('User auth from {}, go there change password'.format(
user.source))
return self.get(request, errors=error) return self.get(request, errors=error)
else: else:
send_reset_password_mail(user) send_reset_password_mail(user)
return HttpResponseRedirect( return redirect('users:forgot-password-sendmail-success')
reverse('users:forgot-password-sendmail-success'))
class UserForgotPasswordSendmailSuccessView(TemplateView): class UserForgotPasswordSendmailSuccessView(TemplateView):
...@@ -79,44 +81,47 @@ class UserResetPasswordSuccessView(TemplateView): ...@@ -79,44 +81,47 @@ class UserResetPasswordSuccessView(TemplateView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class UserResetPasswordView(TemplateView): class UserResetPasswordView(FormView):
template_name = 'users/reset_password.html' template_name = 'users/reset_password.html'
form_class = forms.UserTokenResetPasswordForm
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
token = request.GET.get('token', '') context = self.get_context_data(**kwargs)
errors = kwargs.get('errors')
if errors:
context['errors'] = errors
return self.render_to_response(context)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
token = self.request.GET.get('token', '')
user = User.validate_reset_password_token(token) user = User.validate_reset_password_token(token)
if not user: if not user:
kwargs.update({'errors': _('Token invalid or expired')}) context['errors'] = _('Token invalid or expired')
else: context['token_invalid'] = True
check_rules = get_password_check_rules() check_rules = get_password_check_rules()
kwargs.update({'password_check_rules': check_rules}) context['password_check_rules'] = check_rules
return super().get(request, *args, **kwargs) return context
def post(self, request, *args, **kwargs):
password = request.POST.get('password')
password_confirm = request.POST.get('password-confirm')
token = request.GET.get('token')
if password != password_confirm:
return self.get(request, errors=_('Password not same'))
def form_valid(self, form):
token = self.request.GET.get('token')
user = User.validate_reset_password_token(token) user = User.validate_reset_password_token(token)
if not user: if not user:
return self.get(request, errors=_('Token invalid or expired')) return self.get(self.request, errors=_('Token invalid or expired'))
if not user.can_update_password(): if not user.can_update_password():
error = _('User auth from {}, go there change password'.format(user.source)) errors = _('User auth from {}, go there change password'.format(user.source))
return self.get(request, errors=error) return self.get(self.request, errors=errors)
password = form.cleaned_data['new_password']
is_ok = check_password_rules(password) is_ok = check_password_rules(password)
if not is_ok: if not is_ok:
return self.get( errors = _('* Your password does not meet the requirements')
request, return self.get(self.request, errors=errors)
errors=_('* Your password does not meet the requirements')
)
user.reset_password(password) user.reset_password(password)
User.expired_reset_password_token(token) User.expired_reset_password_token(token)
return HttpResponseRedirect(reverse('users:reset-password-success')) return redirect('users:reset-password-success')
class UserFirstLoginView(PermissionsMixin, SessionWizardView): class UserFirstLoginView(PermissionsMixin, SessionWizardView):
...@@ -177,5 +182,4 @@ class UserFirstLoginView(PermissionsMixin, SessionWizardView): ...@@ -177,5 +182,4 @@ class UserFirstLoginView(PermissionsMixin, SessionWizardView):
choices = [(k, v) for k, v in choices if k in [0, 1]] choices = [(k, v) for k, v in choices if k in [0, 1]]
form.fields["mfa_level"].choices = choices form.fields["mfa_level"].choices = choices
form.fields["mfa_level"].initial = self.request.user.mfa_level form.fields["mfa_level"].initial = self.request.user.mfa_level
return form return form
...@@ -31,9 +31,9 @@ __all__ = [ ...@@ -31,9 +31,9 @@ __all__ = [
'UserProfileView', 'UserProfileView',
'UserProfileUpdateView', 'UserPasswordUpdateView', 'UserProfileUpdateView', 'UserPasswordUpdateView',
'UserPublicKeyUpdateView', 'UserPublicKeyGenerateView', 'UserPublicKeyUpdateView', 'UserPublicKeyGenerateView',
'UserOtpEnableAuthenticationView', 'UserOtpEnableInstallAppView', 'UserCheckPasswordView', 'UserOtpEnableInstallAppView',
'UserOtpEnableBindView', 'UserOtpSettingsSuccessView', 'UserOtpEnableBindView', 'UserOtpSettingsSuccessView',
'UserOtpDisableAuthenticationView', 'UserOtpUpdateView', 'UserDisableMFAView', 'UserOtpUpdateView',
] ]
logger = get_logger(__name__) logger = get_logger(__name__)
...@@ -140,24 +140,10 @@ class UserPublicKeyGenerateView(PermissionsMixin, View): ...@@ -140,24 +140,10 @@ class UserPublicKeyGenerateView(PermissionsMixin, View):
return response return response
class UserOtpEnableAuthenticationView(FormView): class UserCheckPasswordView(FormView):
template_name = 'users/user_password_authentication.html' template_name = 'users/user_password_check.html'
form_class = forms.UserCheckPasswordForm form_class = forms.UserCheckPasswordForm
def get_form(self, form_class=None):
user = get_user_or_tmp_user(self.request)
form = super().get_form(form_class=form_class)
form['username'].initial = user.username
return form
def get_context_data(self, **kwargs):
user = get_user_or_tmp_user(self.request)
context = {
'user': user
}
kwargs.update(context)
return super().get_context_data(**kwargs)
def form_valid(self, form): def form_valid(self, form):
user = get_user_or_tmp_user(self.request) user = get_user_or_tmp_user(self.request)
password = form.cleaned_data.get('password') password = form.cleaned_data.get('password')
...@@ -165,10 +151,17 @@ class UserOtpEnableAuthenticationView(FormView): ...@@ -165,10 +151,17 @@ class UserOtpEnableAuthenticationView(FormView):
if not user: if not user:
form.add_error("password", _("Password invalid")) form.add_error("password", _("Password invalid"))
return self.form_invalid(form) return self.form_invalid(form)
if not user.mfa_is_otp():
user.enable_mfa()
user.save()
return redirect(self.get_success_url()) return redirect(self.get_success_url())
def get_success_url(self): def get_success_url(self):
return reverse('users:user-otp-enable-install-app') if settings.OTP_IN_RADIUS:
success_url = reverse_lazy('users:user-otp-settings-success')
else:
success_url = reverse('users:user-otp-enable-install-app')
return success_url
class UserOtpEnableInstallAppView(TemplateView): class UserOtpEnableInstallAppView(TemplateView):
...@@ -213,23 +206,23 @@ class UserOtpEnableBindView(TemplateView, FormView): ...@@ -213,23 +206,23 @@ class UserOtpEnableBindView(TemplateView, FormView):
def save_otp(self, otp_secret_key): def save_otp(self, otp_secret_key):
user = get_user_or_tmp_user(self.request) user = get_user_or_tmp_user(self.request)
user.enable_otp() user.enable_mfa()
user.otp_secret_key = otp_secret_key user.otp_secret_key = otp_secret_key
user.save() user.save()
class UserOtpDisableAuthenticationView(FormView): class UserDisableMFAView(FormView):
template_name = 'users/user_otp_authentication.html' template_name = 'users/user_disable_mfa.html'
form_class = forms.UserCheckOtpCodeForm form_class = forms.UserCheckOtpCodeForm
success_url = reverse_lazy('users:user-otp-settings-success') success_url = reverse_lazy('users:user-otp-settings-success')
def form_valid(self, form): def form_valid(self, form):
user = self.request.user user = self.request.user
otp_code = form.cleaned_data.get('otp_code') otp_code = form.cleaned_data.get('otp_code')
otp_secret_key = user.otp_secret_key
if check_otp_code(otp_secret_key, otp_code): valid = user.check_mfa(otp_code)
user.disable_otp() if valid:
user.disable_mfa()
user.save() user.save()
return super().form_valid(form) return super().form_valid(form)
else: else:
...@@ -237,16 +230,13 @@ class UserOtpDisableAuthenticationView(FormView): ...@@ -237,16 +230,13 @@ class UserOtpDisableAuthenticationView(FormView):
return super().form_invalid(form) return super().form_invalid(form)
class UserOtpUpdateView(UserOtpDisableAuthenticationView): class UserOtpUpdateView(UserDisableMFAView):
success_url = reverse_lazy('users:user-otp-enable-bind') success_url = reverse_lazy('users:user-otp-enable-bind')
class UserOtpSettingsSuccessView(TemplateView): class UserOtpSettingsSuccessView(TemplateView):
template_name = 'flash_message_standalone.html' template_name = 'flash_message_standalone.html'
# def get(self, request, *args, **kwargs):
# return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
title, describe = self.get_title_describe() title, describe = self.get_title_describe()
context = { context = {
...@@ -265,8 +255,9 @@ class UserOtpSettingsSuccessView(TemplateView): ...@@ -265,8 +255,9 @@ class UserOtpSettingsSuccessView(TemplateView):
auth_logout(self.request) auth_logout(self.request)
title = _('MFA enable success') title = _('MFA enable success')
describe = _('MFA enable success, return login page') describe = _('MFA enable success, return login page')
if not user.otp_enabled: if not user.mfa_enabled:
title = _('MFA disable success') title = _('MFA disable success')
describe = _('MFA disable success, return login page') describe = _('MFA disable success, return login page')
return title, describe return title, describe
...@@ -19,7 +19,7 @@ from daemon import pidfile ...@@ -19,7 +19,7 @@ from daemon import pidfile
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) BASE_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, BASE_DIR) sys.path.insert(0, BASE_DIR)
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
try: try:
from apps.jumpserver import const from apps.jumpserver import const
......
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