[Update] 修改 RemoteApp 前端 Form 渲染逻辑 (#3523)

* [Update] 修改 RemoteApp 前端 Form 渲染逻辑

* [Update] RemoteApp 表单添加默认值
pull/3524/head
BaiJiangJie 5 years ago committed by GitHub
parent b2932803b0
commit b4498f2267
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -12,29 +12,6 @@ REMOTE_APP_TYPE_MYSQL_WORKBENCH = 'mysql_workbench'
REMOTE_APP_TYPE_VMWARE_CLIENT = 'vmware_client'
REMOTE_APP_TYPE_CUSTOM = 'custom'
REMOTE_APP_TYPE_CHOICES = (
(
_('Browser'),
(
(REMOTE_APP_TYPE_CHROME, 'Chrome'),
)
),
(
_('Database tools'),
(
(REMOTE_APP_TYPE_MYSQL_WORKBENCH, 'MySQL Workbench'),
)
),
(
_('Virtualization tools'),
(
(REMOTE_APP_TYPE_VMWARE_CLIENT, 'vSphere Client'),
)
),
(REMOTE_APP_TYPE_CUSTOM, _('Custom')),
)
# Fields attribute write_only default => False
REMOTE_APP_TYPE_CHROME_FIELDS = [
@ -60,9 +37,16 @@ REMOTE_APP_TYPE_CUSTOM_FIELDS = [
{'name': 'custom_password', 'write_only': True}
]
REMOTE_APP_TYPE_MAP_FIELDS = {
REMOTE_APP_TYPE_FIELDS_MAP = {
REMOTE_APP_TYPE_CHROME: REMOTE_APP_TYPE_CHROME_FIELDS,
REMOTE_APP_TYPE_MYSQL_WORKBENCH: REMOTE_APP_TYPE_MYSQL_WORKBENCH_FIELDS,
REMOTE_APP_TYPE_VMWARE_CLIENT: REMOTE_APP_TYPE_VMWARE_CLIENT_FIELDS,
REMOTE_APP_TYPE_CUSTOM: REMOTE_APP_TYPE_CUSTOM_FIELDS
}
REMOTE_APP_TYPE_CHOICES = (
(REMOTE_APP_TYPE_CHROME, 'Chrome'),
(REMOTE_APP_TYPE_MYSQL_WORKBENCH, 'MySQL Workbench'),
(REMOTE_APP_TYPE_VMWARE_CLIENT, 'vSphere Client'),
(REMOTE_APP_TYPE_CUSTOM, _('Custom')),
)

@ -5,18 +5,52 @@ from django.utils.translation import ugettext as _
from django import forms
from orgs.mixins.forms import OrgModelForm
from assets.models import SystemUser
from ..models import RemoteApp
from .. import const
__all__ = [
'RemoteAppCreateUpdateForm',
'RemoteAppChromeForm', 'RemoteAppMySQLWorkbenchForm',
'RemoteAppVMwareForm', 'RemoteAppCustomForm'
]
class RemoteAppTypeChromeForm(forms.ModelForm):
class BaseRemoteAppForm(OrgModelForm):
default_initial_data = {}
def __init__(self, *args, **kwargs):
# 过滤RDP资产和系统用户
super().__init__(*args, **kwargs)
field_asset = self.fields['asset']
field_asset.queryset = field_asset.queryset.has_protocol('rdp')
self.fields['type'].widget.attrs['disabled'] = True
self.fields.move_to_end('comment')
self.initial_default()
def initial_default(self):
for name, value in self.default_initial_data.items():
field = self.fields.get(name)
if not field:
continue
field.initial = value
class Meta:
model = RemoteApp
fields = [
'name', 'asset', 'type', 'path', 'comment'
]
widgets = {
'asset': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Asset')
}),
}
class RemoteAppChromeForm(BaseRemoteAppForm):
default_initial_data = {
'path': r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
}
chrome_target = forms.CharField(
max_length=128, label=_('Target URL'), required=False
)
@ -29,7 +63,12 @@ class RemoteAppTypeChromeForm(forms.ModelForm):
)
class RemoteAppTypeMySQLWorkbenchForm(forms.ModelForm):
class RemoteAppMySQLWorkbenchForm(BaseRemoteAppForm):
default_initial_data = {
'path': r'C:\Program Files\MySQL\MySQL Workbench 8.0 CE'
r'\MySQLWorkbench.exe'
}
mysql_workbench_ip = forms.CharField(
max_length=128, label=_('Database IP'), required=False
)
@ -45,7 +84,12 @@ class RemoteAppTypeMySQLWorkbenchForm(forms.ModelForm):
)
class RemoteAppTypeVMwareForm(forms.ModelForm):
class RemoteAppVMwareForm(BaseRemoteAppForm):
default_initial_data = {
'path': r'C:\Program Files (x86)\VMware\Infrastructure'
r'\Virtual Infrastructure Client\Launcher\VpxClient.exe'
}
vmware_target = forms.CharField(
max_length=128, label=_('Target address'), required=False
)
@ -58,7 +102,8 @@ class RemoteAppTypeVMwareForm(forms.ModelForm):
)
class RemoteAppTypeCustomForm(forms.ModelForm):
class RemoteAppCustomForm(BaseRemoteAppForm):
custom_cmdline = forms.CharField(
max_length=128, label=_('Operating parameter'), required=False
)
@ -73,51 +118,3 @@ class RemoteAppTypeCustomForm(forms.ModelForm):
max_length=128, label=_('Login password'), required=False
)
class RemoteAppTypeForms(
RemoteAppTypeChromeForm,
RemoteAppTypeMySQLWorkbenchForm,
RemoteAppTypeVMwareForm,
RemoteAppTypeCustomForm
):
pass
class RemoteAppCreateUpdateForm(RemoteAppTypeForms, OrgModelForm):
def __init__(self, *args, **kwargs):
# 过滤RDP资产和系统用户
super().__init__(*args, **kwargs)
field_asset = self.fields['asset']
field_asset.queryset = field_asset.queryset.has_protocol('rdp')
class Meta:
model = RemoteApp
fields = [
'name', 'asset', 'type', 'path', 'comment'
]
widgets = {
'asset': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Asset')
}),
}
def _clean_params(self):
app_type = self.data.get('type')
fields = const.REMOTE_APP_TYPE_MAP_FIELDS.get(app_type, [])
params = {}
for field in fields:
name = field['name']
value = self.cleaned_data[name]
params.update({name: value})
return params
def _save_params(self, instance):
params = self._clean_params()
instance.params = params
instance.save()
return instance
def save(self, commit=True):
instance = super().save(commit=commit)
instance = self._save_params(instance)
return instance

@ -62,7 +62,7 @@ class RemoteApp(OrgModelMixin):
_parameters.append(self.type)
path = '\"%s\"' % self.path
_parameters.append(path)
for field in const.REMOTE_APP_TYPE_MAP_FIELDS[self.type]:
for field in const.REMOTE_APP_TYPE_FIELDS_MAP[self.type]:
value = self.params.get(field['name'])
if value is None:
continue

@ -1,7 +1,7 @@
# coding: utf-8
#
import copy
from rest_framework import serializers
from common.serializers import AdaptedBulkListSerializer
@ -18,26 +18,53 @@ __all__ = [
class RemoteAppParamsDictField(CustomMetaDictField):
type_map_fields = const.REMOTE_APP_TYPE_MAP_FIELDS
type_fields_map = const.REMOTE_APP_TYPE_FIELDS_MAP
default_type = const.REMOTE_APP_TYPE_CHROME
convert_key_remove_type_prefix = False
convert_key_to_upper = False
class RemoteAppSerializer(BulkOrgResourceModelSerializer):
params = RemoteAppParamsDictField()
type_fields_map = const.REMOTE_APP_TYPE_FIELDS_MAP
class Meta:
model = RemoteApp
list_serializer_class = AdaptedBulkListSerializer
fields = [
'id', 'name', 'asset', 'type', 'path', 'params',
'comment', 'created_by', 'date_created', 'asset_info',
'get_type_display',
'id', 'name', 'asset', 'asset_info', 'type', 'get_type_display',
'path', 'params', 'date_created', 'created_by', 'comment',
]
read_only_fields = [
'created_by', 'date_created', 'asset_info',
'get_type_display'
]
def process_params(self, instance, validated_data):
new_params = copy.deepcopy(validated_data.get('params', {}))
tp = validated_data.get('type', '')
if tp != instance.type:
return new_params
old_params = instance.params
fields = self.type_fields_map.get(instance.type, [])
for field in fields:
if not field.get('write_only', False):
continue
field_name = field['name']
new_value = new_params.get(field_name, '')
old_value = old_params.get(field_name, '')
field_value = new_value if new_value else old_value
new_params[field_name] = field_value
return new_params
def update(self, instance, validated_data):
params = self.process_params(instance, validated_data)
validated_data['params'] = params
return super().update(instance, validated_data)
class RemoteAppConnectionInfoSerializer(serializers.ModelSerializer):
parameter_remote_app = serializers.SerializerMethodField()

@ -4,51 +4,8 @@
{% load i18n %}
{% block form %}
<form id="appForm" method="post" class="form-horizontal">
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
{% csrf_token %}
{% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.asset layout="horizontal" %}
{% bootstrap_field form.type layout="horizontal" %}
{% bootstrap_field form.path layout="horizontal" %}
<div class="hr-line-dashed"></div>
{# chrome #}
<div class="chrome-fields hidden">
{% bootstrap_field form.chrome_target layout="horizontal" %}
{% bootstrap_field form.chrome_username layout="horizontal" %}
{% bootstrap_field form.chrome_password layout="horizontal" %}
</div>
{# mysql workbench #}
<div class="mysql_workbench-fields hidden">
{% bootstrap_field form.mysql_workbench_ip layout="horizontal" %}
{% bootstrap_field form.mysql_workbench_name layout="horizontal" %}
{% bootstrap_field form.mysql_workbench_username layout="horizontal" %}
{% bootstrap_field form.mysql_workbench_password layout="horizontal" %}
</div>
{# vmware #}
<div class="vmware_client-fields hidden">
{% bootstrap_field form.vmware_target layout="horizontal" %}
{% bootstrap_field form.vmware_username layout="horizontal" %}
{% bootstrap_field form.vmware_password layout="horizontal" %}
</div>
{# custom #}
<div class="custom-fields hidden">
{% bootstrap_field form.custom_cmdline layout="horizontal" %}
{% bootstrap_field form.custom_target layout="horizontal" %}
{% bootstrap_field form.custom_username layout="horizontal" %}
{% bootstrap_field form.custom_password layout="horizontal" %}
</div>
{% bootstrap_field form.comment layout="horizontal" %}
<form id="RemoteAppForm" method="post" class="form-horizontal">
{% bootstrap_form form layout="horizontal" %}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
@ -57,93 +14,49 @@
<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">
var app_type_id = '#' + '{{ form.type.id_for_label }}';
var app_path_id = '#' + '{{ form.path.id_for_label }}';
var all_type_fields = [
'.chrome-fields',
'.mysql_workbench-fields',
'.vmware_client-fields',
'.custom-fields'
];
var app_type_map_default_fields_value = {
'chrome': {
'app_path': 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'
},
'mysql_workbench': {
'app_path': 'C:\\Program Files\\MySQL\\MySQL Workbench 8.0 CE\\MySQLWorkbench.exe'
},
'vmware_client': {
'app_path': 'C:\\Program Files (x86)\\VMware\\Infrastructure\\Virtual Infrastructure Client\\Launcher\\VpxClient.exe'
},
'custom': {'app_path': ''}
};
function getAppType(){
function getFormDataType(){
return $(app_type_id+ " option:selected").val();
}
function initialDefaultValue(){
var app_type = getAppType();
var app_path = $(app_path_id).val();
if(app_path){
app_type_map_default_fields_value[app_type]['app_path'] = app_path
}
}
function setDefaultValue(){
// 设置类型相关字段的默认值
var app_type = getAppType();
var app_path = app_type_map_default_fields_value[app_type]['app_path'];
$(app_path_id).val(app_path)
}
function hiddenFields(){
var app_type = getAppType();
$.each(all_type_fields, function(index, value){
$(value).addClass('hidden')
});
$('.' + app_type + '-fields').removeClass('hidden');
}
function constructParams(data) {
var typeList = ['chrome', 'mysql_workbench', 'vmware_client', 'custom'];
function constructFormDataParams(data){
var params = {};
$.each(typeList, function(index, value){
if (data.type === value){
for (var k in data){
if (k.startsWith(value)){
params[k] = data[k]
}
}
var type =data.type;
for (var k in data){
if (k.startsWith(type)){
params[k] = data[k];
delete data[k]
}
});
return params;
}
return params
}
function getFormData(form){
var data = form.serializeObject();
data['type'] = getFormDataType();
data['params'] = constructFormDataParams(data);
return data
}
$(document).ready(function () {
$('.select2').select2({
closeOnSelect: true
});
initialDefaultValue();
hiddenFields();
setDefaultValue();
})
.on('change', app_type_id, function(){
hiddenFields();
setDefaultValue();
})
.on("submit", "form", function (evt) {
}).on("submit", "form", function (evt) {
evt.preventDefault();
var the_url = '{% url "api-applications:remote-app-list" %}';
var redirect_to = '{% url "applications:remote-app-list" %}';
var method = "POST";
{% if type == "update" %}
{% if api_action == "update" %}
the_url = '{% url "api-applications:remote-app-detail" object.id %}';
method = "PUT";
{% endif %}
var form = $("form");
var data = form.serializeObject();
data["params"] = constructParams(data);
var data = getFormData(form);
var props = {
url: the_url,
data: data,

@ -6,8 +6,16 @@
{% endblock %}
{% block table_search %}{% endblock %}
{% block table_container %}
<div class="uc pull-left m-r-5">
<a href="{% url 'applications:remote-app-create' %}" class="btn btn-sm btn-primary"> {% trans "Create RemoteApp" %} </a>
<div class="btn-group uc pull-left m-r-5">
<button class="btn btn-sm btn-primary">
{% trans "Create RemoteApp" %}
</button>
<button data-toggle="dropdown" class="btn btn-primary btn-sm dropdown-toggle"><span class="caret"></span></button>
<ul class="dropdown-menu">
{% for key, value in type_choices %}
<li><a class="" href="{% url 'applications:remote-app-create' %}?type={{ key }}">{{ value }}</a></li>
{% endfor %}
</ul>
</div>
<table class="table table-striped table-bordered table-hover " id="remote_app_list_table" >
<thead>

@ -1,19 +1,16 @@
# coding: utf-8
#
from django.http import Http404
from django.utils.translation import ugettext as _
from django.views.generic import TemplateView
from django.views.generic.edit import CreateView, UpdateView
from django.views.generic.detail import DetailView
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from common.permissions import PermissionsMixin, IsOrgAdmin, IsValidUser
from common.const import create_success_msg, update_success_msg
from ..models import RemoteApp
from .. import forms
from .. import forms, const
__all__ = [
@ -30,53 +27,79 @@ class RemoteAppListView(PermissionsMixin, TemplateView):
context = {
'app': _('Applications'),
'action': _('RemoteApp list'),
'type_choices': const.REMOTE_APP_TYPE_CHOICES,
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class RemoteAppCreateView(PermissionsMixin, SuccessMessageMixin, CreateView):
class BaseRemoteAppCreateUpdateView:
template_name = 'applications/remote_app_create_update.html'
model = RemoteApp
form_class = forms.RemoteAppCreateUpdateForm
success_url = reverse_lazy('applications:remote-app-list')
permission_classes = [IsOrgAdmin]
default_type = const.REMOTE_APP_TYPE_CHROME
form_class = forms.RemoteAppChromeForm
form_class_choices = {
const.REMOTE_APP_TYPE_CHROME: forms.RemoteAppChromeForm,
const.REMOTE_APP_TYPE_MYSQL_WORKBENCH: forms.RemoteAppMySQLWorkbenchForm,
const.REMOTE_APP_TYPE_VMWARE_CLIENT: forms.RemoteAppVMwareForm,
const.REMOTE_APP_TYPE_CUSTOM: forms.RemoteAppCustomForm
}
def get_initial(self):
return {'type': self.get_type()}
def get_type(self):
return self.default_type
def get_form_class(self):
tp = self.get_type()
form_class = self.form_class_choices.get(tp)
if not form_class:
raise Http404()
return form_class
class RemoteAppCreateView(BaseRemoteAppCreateUpdateView,
PermissionsMixin, CreateView):
def get_context_data(self, **kwargs):
context = {
'app': _('Applications'),
'action': _('Create RemoteApp'),
'type': 'create'
'api_action': 'create'
}
kwargs.update(context)
return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
return create_success_msg % ({'name': cleaned_data['name']})
def get_type(self):
tp = self.request.GET.get("type")
if tp:
return tp.lower()
return super().get_type()
class RemoteAppUpdateView(PermissionsMixin, SuccessMessageMixin, UpdateView):
template_name = 'applications/remote_app_create_update.html'
model = RemoteApp
form_class = forms.RemoteAppCreateUpdateForm
success_url = reverse_lazy('applications:remote-app-list')
permission_classes = [IsOrgAdmin]
class RemoteAppUpdateView(BaseRemoteAppCreateUpdateView,
PermissionsMixin, UpdateView):
def get_initial(self):
return {k: v for k, v in self.object.params.items()}
initial_data = super().get_initial()
params = {k: v for k, v in self.object.params.items()}
initial_data.update(params)
return initial_data
def get_type(self):
return self.object.type
def get_context_data(self, **kwargs):
context = {
'app': _('Applications'),
'action': _('Update RemoteApp'),
'type': 'update'
'api_action': 'update'
}
kwargs.update(context)
return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
return update_success_msg % ({'name': cleaned_data['name']})
class RemoteAppDetailView(PermissionsMixin, DetailView):
template_name = 'applications/remote_app_detail.html'

@ -51,12 +51,13 @@ class CustomMetaDictField(serializers.DictField):
CommandStorage meta field
ReplayStorage meta field
"""
type_map_fields = {}
type_fields_map = {}
default_type = None
need_convert_key = False
convert_key_remove_type_prefix = False
convert_key_to_upper = False
def filter_attribute(self, attribute, instance):
fields = self.type_map_fields.get(instance.type, [])
fields = self.type_fields_map.get(instance.type, [])
for field in fields:
if field.get('write_only', False):
attribute.pop(field['name'], None)
@ -70,29 +71,35 @@ class CustomMetaDictField(serializers.DictField):
attribute = self.filter_attribute(attribute, instance)
return attribute
def convert_value_key(self, dictionary, value):
if not self.need_convert_key:
# remote app
def convert_value_key_remove_type_prefix(self, dictionary, value):
if not self.convert_key_remove_type_prefix:
return value
tp = dictionary.get('type')
_value = {}
prefix = '{}_'.format(tp)
convert_value = {}
for k, v in value.items():
prefix = '{}_'.format(tp)
_k = k
if k.lower().startswith(prefix):
_k = k.lower().split(prefix, 1)[1]
_k = _k.upper()
_value[_k] = value[k]
return _value
k = k.lower().split(prefix, 1)[1]
convert_value[k] = v
return convert_value
def convert_value_key_to_upper(self, value):
if not self.convert_key_to_upper:
return value
convert_value = {k.upper(): v for k, v in value.items()}
return convert_value
def convert_value_key(self, dictionary, value):
value = self.convert_value_key_remove_type_prefix(dictionary, value)
value = self.convert_value_key_to_upper(value)
return value
def filter_value_key(self, dictionary, value):
tp = dictionary.get('type', self.default_type)
fields = self.type_map_fields.get(tp, [])
tp = dictionary.get('type')
fields = self.type_fields_map.get(tp, [])
fields_names = [field['name'] for field in fields]
no_need_keys = [k for k in value.keys() if k not in fields_names]
for k in no_need_keys:
value.pop(k)
return value
filter_value = {k: v for k, v in value.items() if k in fields_names}
return filter_value
def get_value(self, dictionary):
"""

@ -49,7 +49,7 @@ REPLAY_STORAGE_TYPE_AZURE_FIELDS = [
{'name': 'ENDPOINT_SUFFIX'}
]
REPLAY_STORAGE_TYPE_MAP_FIELDS = {
REPLAY_STORAGE_TYPE_FIELDS_MAP = {
REPLAY_STORAGE_TYPE_NULL: REPLAY_STORAGE_TYPE_EMPTY_FIELDS,
REPLAY_STORAGE_TYPE_SERVER: REPLAY_STORAGE_TYPE_EMPTY_FIELDS,
REPLAY_STORAGE_TYPE_S3: REPLAY_STORAGE_TYPE_S3_FIELDS,
@ -89,7 +89,7 @@ COMMAND_STORAGE_TYPE_ES_FIELDS = [
{'name': 'DOC_TYPE'}
]
COMMAND_STORAGE_TYPE_MAP_FIELDS = {
COMMAND_STORAGE_TYPE_FIELDS_MAP = {
COMMAND_STORAGE_TYPE_NULL: COMMAND_STORAGE_TYPE_EMPTY_FIELDS,
COMMAND_STORAGE_TYPE_SERVER: COMMAND_STORAGE_TYPE_EMPTY_FIELDS,
COMMAND_STORAGE_TYPE_ES: COMMAND_STORAGE_TYPE_ES_FIELDS,

@ -9,13 +9,14 @@ from .. import const
class ReplayStorageMetaDictField(CustomMetaDictField):
type_map_fields = const.REPLAY_STORAGE_TYPE_MAP_FIELDS
type_fields_map = const.REPLAY_STORAGE_TYPE_FIELDS_MAP
default_type = const.REPLAY_STORAGE_TYPE_SERVER
need_convert_key = True
convert_key_remove_type_prefix = True
convert_key_to_upper = True
class BaseStorageSerializerMixin:
type_map_fields = None
type_fields_map = None
def process_meta(self, instance, validated_data):
new_meta = copy.deepcopy(validated_data.get('meta', {}))
@ -25,7 +26,7 @@ class BaseStorageSerializerMixin:
return new_meta
old_meta = instance.meta
fields = self.type_map_fields.get(instance.type, [])
fields = self.type_fields_map.get(instance.type, [])
for field in fields:
if not field.get('write_only', False):
continue
@ -48,7 +49,7 @@ class ReplayStorageSerializer(BaseStorageSerializerMixin,
meta = ReplayStorageMetaDictField()
type_map_fields = const.REPLAY_STORAGE_TYPE_MAP_FIELDS
type_fields_map = const.REPLAY_STORAGE_TYPE_FIELDS_MAP
class Meta:
model = ReplayStorage
@ -56,9 +57,10 @@ class ReplayStorageSerializer(BaseStorageSerializerMixin,
class CommandStorageMetaDictField(CustomMetaDictField):
type_map_fields = const.COMMAND_STORAGE_TYPE_MAP_FIELDS
type_fields_map = const.COMMAND_STORAGE_TYPE_FIELDS_MAP
default_type = const.COMMAND_STORAGE_TYPE_SERVER
need_convert_key = True
convert_key_remove_type_prefix = True
convert_key_to_upper = True
class CommandStorageSerializer(BaseStorageSerializerMixin,
@ -66,7 +68,7 @@ class CommandStorageSerializer(BaseStorageSerializerMixin,
meta = CommandStorageMetaDictField()
type_map_fields = const.COMMAND_STORAGE_TYPE_MAP_FIELDS
type_fields_map = const.COMMAND_STORAGE_TYPE_FIELDS_MAP
class Meta:
model = CommandStorage

Loading…
Cancel
Save