diff --git a/apps/assets/forms.py b/apps/assets/forms.py index 30e48a443448aae04e9456518be0d4c7dd931839..c2d7e9cd448f97a728e5fb5b385a800d7468288a 100644 --- a/apps/assets/forms.py +++ b/apps/assets/forms.py @@ -13,19 +13,24 @@ class AssetCreateForm(forms.ModelForm): class Meta: model = Asset fields = [ - 'hostname', 'ip', 'public_ip', 'port', 'type', 'comment', 'admin_user', - 'idc', 'groups', 'status', 'env', 'is_active' + 'hostname', 'ip', 'public_ip', 'port', 'type', 'comment', + 'admin_user', 'idc', 'groups', 'status', 'env', 'is_active' ] widgets = { - 'groups': forms.SelectMultiple(attrs={'class': 'select2', - 'data-placeholder': _('Select asset groups')}), - 'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select asset admin user')}), + 'groups': forms.SelectMultiple( + attrs={'class': 'select2', + 'data-placeholder': _('Select asset groups')}), + 'admin_user': forms.Select( + attrs={'class': 'select2', + 'data-placeholder': _('Select asset admin user')}), } help_texts = { 'hostname': '* required', 'ip': '* required', - 'system_users': _('System user will be granted for user to login assets (using ansible create automatic)'), - 'admin_user': _('Admin user should be exist on asset already, And have sudo ALL permission'), + 'system_users': _('System user will be granted for user to login ' + 'assets (using ansible create automatic)'), + 'admin_user': _('Admin user should be exist on asset already, ' + 'And have sudo ALL permission'), } def clean_admin_user(self): @@ -43,23 +48,43 @@ class AssetUpdateForm(forms.ModelForm): 'cabinet_pos', 'number', 'comment' ] widgets = { - 'groups': forms.SelectMultiple(attrs={'class': 'select2', - 'data-placeholder': _('Select asset groups')}), - 'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select asset admin user')}), + 'groups': forms.SelectMultiple( + attrs={'class': 'select2', + 'data-placeholder': _('Select asset groups')}), + 'admin_user': forms.Select( + attrs={'class': 'select2', + 'data-placeholder': _('Select asset admin user')}), } help_texts = { 'hostname': '* required', 'ip': '* required', - 'system_users': _('System user will be granted for user to login assets (using ansible create automatic)'), - 'admin_user': _('Admin user should be exist on asset already, And have sudo ALL permission'), + 'system_users': _('System user will be granted for user ' + 'to login assets (using ansible create automatic)'), + 'admin_user': _('Admin user should be exist on asset ' + 'already, And have sudo ALL permission'), } class AssetBulkUpdateForm(forms.ModelForm): + assets = forms.MultipleChoiceField( + required=True, + help_text='* required', + label=_('Select assets'), + choices=[(asset.id, asset.hostname) for asset in Asset.objects.all()], + widget=forms.SelectMultiple( + attrs={ + 'class': 'select2', + 'data-placeholder': _('Select assets') + } + ) + ) + port = forms.IntegerField(min_value=1, max_value=65535, + required=False, label=_('Port')) + class Meta: model = Asset fields = [ - 'port', 'groups', 'admin_user', 'idc', + 'assets', 'port', 'groups', 'admin_user', 'idc', 'type', 'env', 'status', ] widgets = { @@ -71,6 +96,17 @@ class AssetBulkUpdateForm(forms.ModelForm): 'data-placeholder': _('Select asset admin user')}), } + def save(self, commit=True): + cleaned_data = {k: v for k, v in self.cleaned_data.items() if v is not None} + assets_id = cleaned_data.pop('assets') + groups = cleaned_data.pop('groups') + assets = Asset.objects.filter(id__in=assets_id) + assets.update(**cleaned_data) + if groups: + for asset in assets: + asset.groups.set(groups) + return assets + class AssetGroupForm(forms.ModelForm): # See AdminUserForm comment same it diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index 499ad2cf0f75d2cde06057901b7586e763101acd..bba94c749ca3d23f9c9f3518ab360752b653aafd 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -41,10 +41,13 @@ class Asset(models.Model): ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True) hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname')) port = models.IntegerField(default=22, verbose_name=_('Port')) - groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', verbose_name=_('Asset groups')) + groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', + verbose_name=_('Asset groups')) admin_user = models.ForeignKey(AdminUser, null=True, blank=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_("Admin user")) - system_users = models.ManyToManyField(SystemUser, blank=True, related_name='assets', verbose_name=_("System User")) + system_users = models.ManyToManyField(SystemUser, blank=True, + related_name='assets', + verbose_name=_("System User")) idc = models.ForeignKey(IDC, blank=True, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_('IDC'),) is_active = models.BooleanField(default=True, verbose_name=_('Is active')) @@ -52,12 +55,14 @@ class Asset(models.Model): default='Server', verbose_name=_('Asset type'),) env = models.CharField(choices=ENV_CHOICES, max_length=8, blank=True, null=True, default='Prod', verbose_name=_('Asset environment'),) - status = models.CharField(choices=STATUS_CHOICES, max_length=8, null=True, blank=True, + status = models.CharField(choices=STATUS_CHOICES, max_length=12, null=True, blank=True, default='In use', verbose_name=_('Asset status')) # Some information - public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP')) - remote_card_ip = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Remote control card IP')) + public_ip = models.GenericIPAddressField(max_length=32, blank=True, + null=True, verbose_name=_('Public IP')) + remote_card_ip = models.CharField(max_length=16, null=True, blank=True, + verbose_name=_('Remote control card IP')) cabinet_no = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Cabinet number')) cabinet_pos = models.IntegerField(null=True, blank=True, verbose_name=_('Cabinet position')) number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number')) diff --git a/apps/assets/templates/assets/asset_bulk_update.html b/apps/assets/templates/assets/asset_bulk_update.html index 44951778ed0195f9e70967ada0c3f98ef6dcfbb7..e72b443029f40ab6c220222d3f0e870ef4119848 100644 --- a/apps/assets/templates/assets/asset_bulk_update.html +++ b/apps/assets/templates/assets/asset_bulk_update.html @@ -9,32 +9,14 @@ <div class="tagBtnList"> <a class="label label-primary" id="change_all" value="1">全选</a> {% for field in form %} + {% if field.name != 'assets' %} <a data-id="{{ field.id_for_label }}" class="label label-default label-primary field-tag" value="1">{{ field.label }}</a> + {% endif %} {% endfor %} </div> </div> -{% if errors %} - <div class="alert alert-danger"> - {{ errors }} - </div> -{% endif %} <form method="post" class="form-horizontal" id="add_form"> {% csrf_token %} - <div class="form-group abc"> - <label class="control-label col-sm-2 col-lg-2 " id="asset_on_count">{% trans 'Asset' %}</label> - <div class="col-sm-9"> - <select class="form-control select2" multiple="multiple" name="assets"> - {% for asset in assets %} - {% if asset.id in assets_selected %} - <option selected="selected" value="{{ asset.id }}">{{ asset.hostname }}</option> - {% else %} - <option value="{{ asset.id }}">{{ asset.hostname }}</option> - {% endif %} - {% endfor %} - </select> - <span class="help-block">* required</span> - </div> - </div> {% bootstrap_form form layout="horizontal" %} <div class="form-group abc"> <div class="col-sm-4 col-sm-offset-2"> @@ -83,33 +65,5 @@ form_groups.filter(':has(#' + field_id + ')').hide().find('select,input').prop('disabled', true) } } - - function fsubmit(){ - var assets_id = document.getElementsByName("assets"); - var oForm = document.getElementById('add_form'); - var parentElem = document.getElementById("add_form"); - var aDiv = parentElem.getElementsByClassName('form-group'); - if (assets_id.length === 0) { - swal({ - title: "未选择需è¦ä¿®æ”¹çš„主机", - text: "请点击选择" - }); - }else if (aDiv.length === 1) { - swal({ - title: "未选需è¦ä¿®æ”¹çš„属性", - text: "请点击选择" - }); - }else{ - var m = document.getElementsByName('assets_ids'); - alert(m.length); - for(var i=0;i<m.length;i++){ - alert(m[0].value); - oForm.appendChild(m[0]); - } - action="/assets/asset/"+assets_id[0].value+"/update"; - oForm.action=action; - oForm.submit(); - } - } </script> {% endblock %} diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index 3bfa068c1f7f6bba220bc3cab80ecbebea2cdf02..873acebbf8b04c258bdae780faafaab418318de7 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -88,6 +88,7 @@ function tagShow() { } //onload; + $(document).ready(function(){ var options = { ele: $('#asset_list_table'), @@ -125,6 +126,7 @@ $(document).ready(function(){ op_html: $('#actions').html() }; var table = jumpserver.initDataTable(options); + $('.btn_export').click(function () { var assets = []; var rows = table.rows('.selected').data(); @@ -178,20 +180,18 @@ $(document).ready(function(){ $data_table.ajax.reload(); }, 3000); }) - .on('click', '#btn_bulk_update', function () { var action = $('#slct_bulk_update').val(); var $data_table = $('#asset_list_table').DataTable(); var id_list = []; - 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); + id_list.push(this.data().id); }); - if (plain_id_list.length == 0) { + if (id_list.length == 0) { return false; } var the_url = "{% url 'api-assets:asset-list' %}"; + function doDeactive() { var body = $.each(id_list, function(index, asset_object) { asset_object['is_active'] = false; @@ -234,7 +234,9 @@ $(document).ready(function(){ }); } function doUpdate() { - $('#asset_bulk_update_modal').modal('show'); + var id_list_string = id_list.join(','); + var url = "{% url 'assets:asset-bulk-update' %}?assets_id=" + id_list_string; + location.href = url } switch(action) { case 'deactive': @@ -252,66 +254,66 @@ $(document).ready(function(){ default: break; } -}) - -.on('click', '#btn_asset_bulk_update', function () { - var json_data = $("#fm_asset_bulk_update").serializeObject(); - var body = {}; - body.enable_otp = (json_data.enable_otp === 'on')? true: false; - if (json_data.type != '') { - body.type = json_data.type; - } - if (json_data.groups != undefined) { - body.groups = json_data.groups; - } - if (typeof body.groups === 'string') { - body.groups = [parseInt(body.groups)] - } else if(typeof body.groups === 'array') { - var new_groups = body.groups.map(Number); - body.groups = new_groups; - } - - if (json_data.system_users != undefined) { - body.system_users = json_data.system_users; - } - if (typeof body.system_users === 'string') { - body.system_users = [parseInt(body.system_users)] - } else if(typeof body.system_users === 'array') { - var new_users = body.system_users.map(Number); - body.system_users = new_users; - } - - if (json_data.tags != undefined) { - body.tags = json_data.tags; - } - if (typeof body.tags == 'string') { - body.tags = [parseInt(body.tags)]; - } else if (typeof body.tags === 'array') { - var new_tags = body.tags.map(Number); - body.tags = new_tags; - } - - var $data_table = $('#asset_list_table').DataTable(); - var post_list = []; - $data_table.rows({selected: true}).every(function(){ - var content = Object.assign({id: this.data().id}, body); - post_list.push(content); - }); - if (post_list === []) { - return false - } - var the_url = "{% url 'api-assets:asset-list' %}"; - var success = function() { - var msg = "{% trans 'The selected assets has been updated successfully.' %}"; - swal("{% trans 'Asset Updated' %}", msg, "success"); - $('#asset_list_table').DataTable().ajax.reload(); - jumpserver.checked = false; - }; - console.log(JSON.stringify(post_list)); - console.log(the_url); -{# APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});#} - $('#asset_bulk_update_modal').modal('hide'); }); +{##} +{#.on('click', '#btn_asset_bulk_update', function () {#} +{# var json_data = $("#fm_asset_bulk_update").serializeObject();#} +{# var body = {};#} +{# body.enable_otp = (json_data.enable_otp === 'on')? true: false;#} +{# if (json_data.type != '') {#} +{# body.type = json_data.type;#} +{# }#} +{# if (json_data.groups != undefined) {#} +{# body.groups = json_data.groups;#} +{# }#} +{# if (typeof body.groups === 'string') {#} +{# body.groups = [parseInt(body.groups)]#} +{# } else if(typeof body.groups === 'array') {#} +{# var new_groups = body.groups.map(Number);#} +{# body.groups = new_groups;#} +{# }#} +{##} +{# if (json_data.system_users != undefined) {#} +{# body.system_users = json_data.system_users;#} +{# }#} +{# if (typeof body.system_users === 'string') {#} +{# body.system_users = [parseInt(body.system_users)]#} +{# } else if(typeof body.system_users === 'array') {#} +{# var new_users = body.system_users.map(Number);#} +{# body.system_users = new_users;#} +{# }#} +{##} +{# if (json_data.tags != undefined) {#} +{# body.tags = json_data.tags;#} +{# }#} +{# if (typeof body.tags == 'string') {#} +{# body.tags = [parseInt(body.tags)];#} +{# } else if (typeof body.tags === 'array') {#} +{# var new_tags = body.tags.map(Number);#} +{# body.tags = new_tags;#} +{# }#} +{##} +{# var $data_table = $('#asset_list_table').DataTable();#} +{# var post_list = [];#} +{# $data_table.rows({selected: true}).every(function(){#} +{# var content = Object.assign({id: this.data().id}, body);#} +{# post_list.push(content);#} +{# });#} +{# if (post_list === []) {#} +{# return false#} +{# }#} +{# var the_url = "{% url 'api-assets:asset-list' %}";#} +{# var success = function() {#} +{# var msg = "{% trans 'The selected assets has been updated successfully.' %}";#} +{# swal("{% trans 'Asset Updated' %}", msg, "success");#} +{# $('#asset_list_table').DataTable().ajax.reload();#} +{# jumpserver.checked = false;#} +{# };#} +{# console.log(JSON.stringify(post_list));#} +{# console.log(the_url);#} +{# APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});#} +{# $('#asset_bulk_update_modal').modal('hide');#} +{#})#} </script> {% endblock %} diff --git a/apps/assets/views/asset.py b/apps/assets/views/asset.py index 39ce7e0ddf8eb5344f4b6c076ed1277f3505333b..177e347984f125dd844110701ff8f1cc3dfcd468 100644 --- a/apps/assets/views/asset.py +++ b/apps/assets/views/asset.py @@ -20,7 +20,7 @@ from django.utils.decorators import method_decorator from django.core.cache import cache from django.utils import timezone from django.contrib.auth.mixins import LoginRequiredMixin -from django.shortcuts import get_object_or_404, redirect +from django.shortcuts import get_object_or_404, redirect, reverse from common.mixins import JSONResponseMixin from common.utils import get_object_or_none @@ -43,7 +43,7 @@ class AssetListView(AdminUserRequiredMixin, TemplateView): def get_context_data(self, **kwargs): context = { 'app': 'Assets', - 'action': 'asset list', + 'action': 'Asset list', 'groups': AssetGroup.objects.all(), 'system_users': SystemUser.objects.all(), # 'form': forms.AssetBulkUpdateForm(), @@ -58,7 +58,7 @@ class UserAssetListView(LoginRequiredMixin, TemplateView): def get_context_data(self, **kwargs): context = { 'app': 'Assets', - 'action': 'asset list', + 'action': 'Asset list', 'system_users': SystemUser.objects.all(), } kwargs.update(context) @@ -118,62 +118,24 @@ class AssetBulkUpdateView(AdminUserRequiredMixin, ListView): def get(self, request, *args, **kwargs): assets_id = self.request.GET.get('assets_id', '') self.assets_id_list = [int(i) for i in assets_id.split(',') if i.isdigit()] - self.form = self.form_class() - self.errors = kwargs.get('errors') + + if kwargs.get('form'): + self.form = kwargs['form'] + elif assets_id: + self.form = self.form_class( + initial={'assets': self.assets_id_list} + ) + else: + self.form = self.form_class() return super(AssetBulkUpdateView, self).get(request, *args, **kwargs) def post(self, request, *args, **kwargs): - raw_data = request.POST - data = {} - errors = defaultdict(list) - for k in raw_data: - if not hasattr(Asset, k) or raw_data.get(k) == '': - if k not in ['assets']: - continue - if k == 'assets': - v = Asset.objects.filter(id__in=raw_data.getlist(k)) - if not v: - errors['assets'].append(_('Required')) - elif k == 'port': - try: - v = int(raw_data.get(k)) - except ValueError: - v = None - errors['port'].append(_('Integer required')) - elif k == 'admin_user': - admin_user_id = raw_data.get(k) - try: - v = int(admin_user_id) - except ValueError: - v = None - errors['admin_user'].append(_('Invalid admin user')) - v = get_object_or_none(AdminUser, id=v) - elif k == 'groups': - groups_id = raw_data.getlist(k) - v = [AssetGroup.objects.filter(id__in=groups_id)] - elif k == 'idc': - idc_id = raw_data.get(k) - try: - v = int(idc_id) - except ValueError: - v = None - errors['idc'].append(_('Integer required')) - v = get_object_or_none(IDC, id=v) - else: - v = raw_data.get(k) - data[k] = v - - if not errors: - for asset in data['assets']: - for k, v in data.items(): - if k == 'groups': - asset.groups.set(data['groups']) - else: - setattr(asset, k, v) - asset.save() - return redirect(reverse_lazy('assets:asset-list')) + form = self.form_class(request.POST) + if form.is_valid(): + form.save() + return redirect(self.success_url) else: - return self.get(request, errors=errors, *args, **kwargs) + return self.get(request, form=form, *args, **kwargs) def get_context_data(self, **kwargs): # assets_list = Asset.objects.filter(id__in=self.assets_id_list) @@ -181,7 +143,6 @@ class AssetBulkUpdateView(AdminUserRequiredMixin, ListView): 'app': 'Assets', 'action': 'Bulk update asset', 'form': self.form, - 'errors': self.errors, 'assets_selected': self.assets_id_list, 'assets': Asset.objects.all(), } @@ -236,9 +197,6 @@ class AssetDetailView(DetailView): return super(AssetDetailView, self).get_context_data(**kwargs) - - - @method_decorator(csrf_exempt, name='dispatch') class AssetExportView(View): def get(self, request, *args, **kwargs): diff --git a/apps/users/forms.py b/apps/users/forms.py index b89b62660efdeaf1ace2b8c1820b7b4e782edf62..a334f7a7e443bdc102e982397e4829c9eb8ce881 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -114,15 +114,44 @@ class UserPublicKeyForm(forms.Form): return self.instance -# class UserBulkImportForm(forms.ModelForm): -# class Meta: -# model = User -# fields = ['username', 'email', 'enable_otp', 'role'] - class UserBulkUpdateForm(forms.ModelForm): + role = forms.ChoiceField( + label=_('Role'), + choices=[('Admin', 'Administrator'), ('User', 'User')], + ) + users = forms.MultipleChoiceField( + required=True, + help_text='* required', + label=_('Select users'), + choices=[(user.id, user.name ) for user in User.objects.all()], + widget=forms.SelectMultiple( + attrs={ + 'class': 'select2', + 'data-placeholder': _('Select users') + } + ) + ) + class Meta: model = User - fields = ['role', 'groups', 'date_expired', 'is_active', 'enable_otp'] + fields = ['users', 'role', 'groups', 'date_expired', 'is_active', 'enable_otp'] + widgets = { + 'groups': forms.SelectMultiple( + attrs={'class': 'select2', + 'data-placeholder': _('Select user groups')}), + } + + def save(self, commit=True): + cleaned_data = {k: v for k, v in self.cleaned_data.items() if + v is not None} + users_id = cleaned_data.pop('users') + groups = cleaned_data.pop('groups') + users = User.objects.filter(id__in=users_id) + users.update(**cleaned_data) + if groups: + for user in users: + user.groups.set(groups) + return users class UserGroupForm(forms.ModelForm): diff --git a/apps/users/templates/users/_user_bulk_update_modal.html b/apps/users/templates/users/_user_bulk_update_modal.html index 8b25a7e0634b39f4286175fb2435d8bd58732b49..c1c5852e6782fea09c54e457a14309de91a8e441 100644 --- a/apps/users/templates/users/_user_bulk_update_modal.html +++ b/apps/users/templates/users/_user_bulk_update_modal.html @@ -5,33 +5,68 @@ {% block modal_class %}modal-lg{% endblock %} {% block modal_title%}{% trans "Update selected user" %}{% endblock %} {% block modal_body %} -<div class="ydxbd" id="ydxbd" style="display: block;"> - <div> - <p id="tags_p"> - <a href="/assets/asset-by-tag/5"> - <span class="label label-default">三年质ä¿(0)</span> - </a> - <a href="/assets/asset-by-tag/5"> - <span class="label label-default">三年质ä¿(0)</span> - </a> - </p> - </div> +{% block form %} +<div class="ydxbd" id="formlists" style="display: block;"> + <p id="tags_p" class="mgl-5 c02">选择需è¦ä¿®æ”¹å±žæ€§</p> + <div class="tagBtnList"> + <a class="label label-primary" id="change_all" value="1">全选</a> + {% for field in form %} +{# {% if field.name != 'assets' %}#} + <a data-id="{{ field.id_for_label }}" class="label label-default label-primary field-tag" value="1">{{ field.label }}</a> +{# {% endif %}#} + {% endfor %} + </div> </div> -<form method="post" class="form-horizontal" action="" id="fm_user_bulk_update"> -{# {% for field in form %}#} -{# <input type="checkbox">#} -{# {% bootstrap_field field layout='horizontal' %}#} -{# {% endfor %}#} - {% bootstrap_form form layout='horizontal' %} - -{# <div class="form-group">#} -{# <div class="col-sm-9 col-lg-9 col-sm-offset-2">#} -{# <div class="checkbox">#} -{# <input type="checkbox" name="enable_otp" checked id="id_enable_otp" disabled><label for="id_enable_otp">{% trans 'Enable-OTP' %}</label>#} -{# </div>#} -{# </div>#} -{# </div>#} +<form method="post" class="form-horizontal" id="add_form"> + {% csrf_token %} + {% bootstrap_form form layout="horizontal" %} + <div class="form-group abc"> + <div class="col-sm-4 col-sm-offset-2"> + <button class="btn btn-white" type="reset">{% trans 'Reset' %}</button> + <button class="btn btn-primary" type="submit">{% trans 'Submit' %}</button> + </div> + </div> </form> {% endblock %} -{% block modal_confirm_id %}btn_user_bulk_update{% endblock %} +{% endblock %} +{#{% block custom_foot_js %}#} +{#<script>#} +{# $(document).ready(function () {#} +{# $('.select2').select2();#} +{# }).on('click', '.field-tag', function() {#} +{# changeField(this);#} +{# }).on('click', '#change_all', function () {#} +{# var tag_fields = $('.field-tag');#} +{# var $this = $(this);#} +{# var active = '1';#} +{# if ($this.attr('value') == '0'){#} +{# active = '0';#} +{# $this.attr('value', '1').addClass('label-primary')#} +{# } else {#} +{# active = '1';#} +{# $this.attr('value', '0').removeClass('label-primary')#} +{# }#} +{# $.each(tag_fields, function (k, v) {#} +{# changeField(v, active)#} +{# })#} +{# });#} +{##} +{# function changeField(obj, active) {#} +{# var $this = $(obj);#} +{# var field_id = $this.data('id');#} +{# if (!active) {#} +{# active = $this.attr('value');#} +{# }#} +{# if (active == '0') {#} +{# $this.attr('value', '1').addClass('label-primary');#} +{# var form_groups = $('#add_form .form-group:not(.abc)');#} +{# form_groups.filter(':has(#' + field_id + ')').show().find('select,input').prop('disabled', false)#} +{# } else {#} +{# $this.attr('value', '0').removeClass('label-primary');#} +{# var form_groups = $('#add_form .form-group:not(.abc)');#} +{# form_groups.filter(':has(#' + field_id + ')').hide().find('select,input').prop('disabled', true)#} +{# }#} +{# }#} +{#</script>#} +{#{% endblock %}#} diff --git a/apps/users/templates/users/user_bulk_update.html b/apps/users/templates/users/user_bulk_update.html new file mode 100644 index 0000000000000000000000000000000000000000..a1ddc6e39fb81fe739f5f458acdd58567e84094e --- /dev/null +++ b/apps/users/templates/users/user_bulk_update.html @@ -0,0 +1,69 @@ +{% extends '_base_create_update.html' %} +{% load static %} +{% load bootstrap3 %} +{% load i18n %} + +{% block form %} +<div class="ydxbd" id="formlists" style="display: block;"> + <p id="tags_p" class="mgl-5 c02">选择需è¦ä¿®æ”¹å±žæ€§</p> + <div class="tagBtnList"> + <a class="label label-primary" id="change_all" value="1">全选</a> + {% for field in form %} + {% if field.name != 'users' %} + <a data-id="{{ field.id_for_label }}" class="label label-default label-primary field-tag" value="1">{{ field.label }}</a> + {% endif %} + {% endfor %} + </div> +</div> +<form method="post" class="form-horizontal" id="add_form"> + {% csrf_token %} + {% bootstrap_form form layout="horizontal" %} + <div class="form-group abc"> + <div class="col-sm-4 col-sm-offset-2"> + <button class="btn btn-white" type="reset">{% trans 'Reset' %}</button> + <button class="btn btn-primary" type="submit">{% trans 'Submit' %}</button> + </div> + </div> +</form> +{% endblock %} + +{% block custom_foot_js %} +<script> + $(document).ready(function () { + $('.select2').select2(); + }).on('click', '.field-tag', function() { + changeField(this); + }).on('click', '#change_all', function () { + var tag_fields = $('.field-tag'); + var $this = $(this); + var active = '1'; + if ($this.attr('value') == '0'){ + active = '0'; + $this.attr('value', '1').addClass('label-primary') + } else { + active = '1'; + $this.attr('value', '0').removeClass('label-primary') + } + $.each(tag_fields, function (k, v) { + changeField(v, active) + }) + }); + + function changeField(obj, active) { + var $this = $(obj); + var field_id = $this.data('id'); + if (!active) { + active = $this.attr('value'); + } + if (active == '0') { + $this.attr('value', '1').addClass('label-primary'); + var form_groups = $('#add_form .form-group'); + form_groups.filter(':has(#' + field_id + ')').show().find('select,input').prop('disabled', false) + } else { + $this.attr('value', '0').removeClass('label-primary'); + var form_groups = $('#add_form .form-group'); + form_groups.filter(':has(#' + field_id + ')').hide().find('select,input').prop('disabled', true) + } + } +</script> +{% endblock %} diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index 01bb473f23b565d9636793fa98de73396f949eae..b7fcde2178ab5ae86be48fb5d372edf508ac6289 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -55,6 +55,22 @@ {% block custom_foot_js %} <script src="{% static 'js/jquery.form.min.js' %}"></script> <script> +function changeField(obj, active) { + var $this = $(obj); + var field_id = $this.data('id'); + if (!active) { + active = $this.attr('value'); + } + if (active == '0') { + $this.attr('value', '1').addClass('label-primary'); + var form_groups = $('#add_form .form-group:not(.abc)'); + form_groups.filter(':has(#' + field_id + ')').show().find('select,input').prop('disabled', false) + } else { + $this.attr('value', '0').removeClass('label-primary'); + var form_groups = $('#add_form .form-group:not(.abc)'); + form_groups.filter(':has(#' + field_id + ')').hide().find('select,input').prop('disabled', true) + } +} function renderTable() { var options = { @@ -193,7 +209,9 @@ $(document).ready(function(){ }); } function doUpdate() { - $('#user_bulk_update_modal').modal('show'); + var users_id = plain_id_list.join(','); + var url = "{% url 'users:user-bulk-update' %}?users_id=" + users_id; + location.href = url } switch(action) { case 'deactive': @@ -249,10 +267,9 @@ $(document).ready(function(){ $('#user_list_table').DataTable().ajax.reload(); jumpserver.checked = false; }; - console.log(body); {# APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});#} $('#user_bulk_update_modal').modal('hide'); -}); +}) </script> {% endblock %} diff --git a/apps/users/urls/views_urls.py b/apps/users/urls/views_urls.py index a60f3a76477d69cf3a9421bf90a11b8971d74c03..23ab12012bc007ae6f0851cb65160475a6f809a2 100644 --- a/apps/users/urls/views_urls.py +++ b/apps/users/urls/views_urls.py @@ -22,14 +22,11 @@ urlpatterns = [ name='reset-password-success'), # Profile - url(r'^profile/$', - views.UserProfileView.as_view(), + url(r'^profile/$', views.UserProfileView.as_view(), name='user-profile'), - url(r'^profile/update/$', - views.UserProfileUpdateView.as_view(), + url(r'^profile/update/$', views.UserProfileUpdateView.as_view(), name='user-profile-update'), - url(r'^profile/password/update/$', - views.UserPasswordUpdateView.as_view(), + url(r'^profile/password/update/$', views.UserPasswordUpdateView.as_view(), name='user-password-update'), url(r'^profile/pubkey/update/$', views.UserPublicKeyUpdateView.as_view(), @@ -48,37 +45,29 @@ urlpatterns = [ url(r'^user/(?P<pk>[0-9]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'), - url(r'^user/(?P<pk>[0-9]+)/login-history', - views.UserDetailView.as_view(), + url(r'^user/(?P<pk>[0-9]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'), - url(r'^user/export/', - views.UserExportView.as_view(), + url(r'^user/export/', views.UserExportView.as_view(), name='user-export'), - url(r'^first-login/$', - views.UserFirstLoginView.as_view(), + url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'), - url(r'^user/import/$', - views.UserBulkImportView.as_view(), + url(r'^user/import/$', views.UserBulkImportView.as_view(), name='user-import'), - url(r'^user/create$', - views.UserCreateView.as_view(), + url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'), - url(r'^user/(?P<pk>[0-9]+)/update$', - views.UserUpdateView.as_view(), + url(r'^user/(?P<pk>[0-9]+)/update$', views.UserUpdateView.as_view(), name='user-update'), + url(r'^user/update$', views.UserBulkUpdateView.as_view(), + name='user-bulk-update'), # User group view - url(r'^user-group$', - views.UserGroupListView.as_view(), + url(r'^user-group$', views.UserGroupListView.as_view(), name='user-group-list'), - url(r'^user-group/(?P<pk>[0-9]+)$', - views.UserGroupDetailView.as_view(), + url(r'^user-group/(?P<pk>[0-9]+)$', views.UserGroupDetailView.as_view(), name='user-group-detail'), - url(r'^user-group/create$', - views.UserGroupCreateView.as_view(), + url(r'^user-group/create$', views.UserGroupCreateView.as_view(), name='user-group-create'), - url(r'^user-group/(?P<pk>[0-9]+)/update$', - views.UserGroupUpdateView.as_view(), + url(r'^user-group/(?P<pk>[0-9]+)/update$', views.UserGroupUpdateView.as_view(), name='user-group-update'), url(r'^user-group/(?P<pk>[0-9]+)/asset-permission$', views.UserGroupAssetPermissionView.as_view(), diff --git a/apps/users/views/user.py b/apps/users/views/user.py index 7a09353be74920d84267549ef3639001479cd321..9a7ab4d3c2b5cca747db20e84c6dcefe673fd20c 100644 --- a/apps/users/views/user.py +++ b/apps/users/views/user.py @@ -38,7 +38,7 @@ __all__ = ['UserListView', 'UserCreateView', 'UserDetailView', 'UserAssetPermissionView', 'UserGrantedAssetView', 'UserExportView', 'UserBulkImportView', 'UserProfileView', 'UserProfileUpdateView', 'UserPasswordUpdateView', - 'UserPublicKeyUpdateView', + 'UserPublicKeyUpdateView', 'UserBulkUpdateView', ] logger = get_logger(__name__) @@ -107,6 +107,46 @@ class UserUpdateView(AdminUserRequiredMixin, UpdateView): return context +class UserBulkUpdateView(AdminUserRequiredMixin, ListView): + model = User + form_class = forms.UserBulkUpdateForm + template_name = 'users/user_bulk_update.html' + success_url = reverse_lazy('users:user-list') + + def get(self, request, *args, **kwargs): + users_id = self.request.GET.get('users_id', '') + self.id_list = [int(i) for i in users_id.split(',') if i.isdigit()] + + if kwargs.get('form'): + self.form = kwargs['form'] + elif users_id: + self.form = self.form_class( + initial={'users': self.id_list} + ) + else: + self.form = self.form_class() + return super(UserBulkUpdateView, self).get(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + form = self.form_class(request.POST) + if form.is_valid(): + form.save() + return redirect(self.success_url) + else: + return self.get(request, form=form, *args, **kwargs) + + def get_context_data(self, **kwargs): + context = { + 'app': 'Assets', + 'action': 'Bulk update asset', + 'form': self.form, + 'users_selected': self.id_list, + 'users': User.objects.all(), + } + kwargs.update(context) + return super(UserBulkUpdateView, self).get_context_data(**kwargs) + + class UserDetailView(AdminUserRequiredMixin, DetailView): model = User template_name = 'users/user_detail.html'