From b0eba9d7bbcc30a57ca16a2ec894e9d0f09ecb4e Mon Sep 17 00:00:00 2001 From: root Date: Wed, 15 Sep 2021 09:40:26 +0200 Subject: [PATCH] Added bulk actions --- admin/src/admin/lib/admin.py | 25 +++- admin/src/admin/lib/keycloak_client.py | 10 ++ .../src/admin/static/js/sysadmin/external.js | 18 +-- admin/src/admin/static/js/users.js | 109 +++++++++++++----- .../admin/static/templates/pages/users.html | 29 +++++ admin/src/admin/views/ApiViews.py | 51 +++++++- 6 files changed, 205 insertions(+), 37 deletions(-) diff --git a/admin/src/admin/lib/admin.py b/admin/src/admin/lib/admin.py index c447b0e..5044f52 100644 --- a/admin/src/admin/lib/admin.py +++ b/admin/src/admin/lib/admin.py @@ -629,7 +629,6 @@ class Admin(): i=i+1 log.warning(' KEYCLOAK GROUPS: Adding group ('+str(i)+'/'+str(total)+'): '+g) ev.increment({'name':g}) - print(g) self.keycloak.add_group_tree(g) total=len(self.external['users']) @@ -1170,6 +1169,22 @@ class Admin(): return True + def enable_users(self,data): + #data={'id':'','username':''} + ev=Events('Bulk actions','Enabling user:',total=len(data)) + for user in data: + ev.increment({'name':user['username'],'data':user['username']}) + self.keycloak.user_enable(user['id']) + self.resync_data() + + def disable_users(self,data): + #data={'id':'','username':''} + ev=Events('Bulk actions','Disabling user:',total=len(data)) + for user in data: + ev.increment({'name':user['username'],'data':user['username']}) + self.keycloak.user_disable(user['id']) + self.resync_data() + def update_moodle_user(self,user_id,user,mdelete,madd): internaluser=[u for u in self.internal['users'] if u['id']==user_id][0] cohorts=self.moodle.get_cohorts() @@ -1244,6 +1259,13 @@ class Admin(): except: log.error(traceback.format_exc()) + def delete_users(self,data): + ev=Events('Bulk actions','Deleting users:',total=len(data)) + for user in data: + ev.increment({'name':user['username'],'data':user['username']}) + self.delete_user(user['id']) + self.resync_data() + def delete_user(self,userid): log.warning('Deleting user moodle, nextcloud keycloak') ev=Events('Deleting user','Deleting from moodle') @@ -1278,7 +1300,6 @@ class Admin(): else: pathpart=pathpart+'.'+part pathslist.append(pathpart) - print(pathslist) ### KEYCLOAK ####################### diff --git a/admin/src/admin/lib/keycloak_client.py b/admin/src/admin/lib/keycloak_client.py index 3820e83..6a24709 100644 --- a/admin/src/admin/lib/keycloak_client.py +++ b/admin/src/admin/lib/keycloak_client.py @@ -197,6 +197,16 @@ class KeycloakClient(): self.connect() return self.keycloak_admin.update_user( user_id, payload) + def user_enable(self,user_id): + payload={"enabled":True} + self.connect() + return self.keycloak_admin.update_user( user_id, payload) + + def user_disable(self,user_id): + payload={"enabled":False} + self.connect() + return self.keycloak_admin.update_user( user_id, payload) + def group_user_remove(self,user_id,group_id): self.connect() return self.keycloak_admin.group_user_remove(user_id,group_id) diff --git a/admin/src/admin/static/js/sysadmin/external.js b/admin/src/admin/static/js/sysadmin/external.js index f9345fd..7f5d9b2 100644 --- a/admin/src/admin/static/js/sysadmin/external.js +++ b/admin/src/admin/static/js/sysadmin/external.js @@ -4,6 +4,7 @@ $(document).on('shown.bs.modal', '#modalAddDesktop', function () { }); $(document).ready(function() { + $('#action_role option[value=""]').prop("selected",true); var path = ""; items = []; document.getElementById('file-upload').addEventListener('change', readFile, false); @@ -26,8 +27,8 @@ $(document).ready(function() { { console.log('SUCCESS') // $("#modalImport").modal('hide'); - // users_table.ajax.reload(); - // groups_table.ajax.reload(); + users_table.ajax.reload(); + groups_table.ajax.reload(); }, error: function(data) { @@ -69,7 +70,7 @@ $(document).ready(function() { } }); }).on('pnotify.cancel', function() { - $('#action_role option[value="none"]').prop("selected",true); + $('#action_role option[value=""]').prop("selected",true); }); }); @@ -102,12 +103,14 @@ $(document).ready(function() { { console.log('SUCCESS') $("#modalImport").modal('hide'); - // users_table.ajax.reload(); - // groups_table.ajax.reload(); + users_table.ajax.reload(); + groups_table.ajax.reload(); }, error: function(xhr, status, error) { var err = eval("(" + xhr.responseText + ")"); alert(JSON.parse(xhr.responseText).msg) + users_table.ajax.reload(); + groups_table.ajax.reload(); } }); } @@ -163,8 +166,9 @@ $(document).ready(function() { } }); }).on('pnotify.cancel', function() { - $('#action_role option[value="none"]').prop("selected",true); - }); + + }); + $('#action_role option[value=""]').prop("selected",true); } ); //DataTable Main renderer diff --git a/admin/src/admin/static/js/users.js b/admin/src/admin/static/js/users.js index ab69a60..52ccafd 100644 --- a/admin/src/admin/static/js/users.js +++ b/admin/src/admin/static/js/users.js @@ -5,6 +5,7 @@ $(document).on('shown.bs.modal', '#modalAddDesktop', function () { $(document).ready(function() { + $('#bulk_actions option[value=""]').prop("selected",true); $.ajax({ type: "GET", "url": "/api/groups", @@ -29,30 +30,6 @@ $(document).ready(function() { } }); - // $.ajax({ - // type: "GET", - // "url": "/api/groups", - // success: function(data) - // { - // data.forEach(element => { - // var groupOrigins = []; - // ['keycloak', 'moodle', 'nextcloud'].forEach(o => { - // if (element[o]) { - // groupOrigins.push(o) - // } - // }) - // $(".groups-select").append( - // '' - // ) - // }); - // $('.groups-select').select2(); - // }, - // error: function(data) - // { - // alert('Something went wrong on our side...') - // } - // }); - $.ajax({ type: "GET", "url": "/api/roles", @@ -81,10 +58,68 @@ $(document).ready(function() { error: function(data) { alert('Something went wrong on our side...') + table.ajax.reload(); } }); }); + $('#bulk_actions').on('change', function () { + action=$(this).val(); + names='' + ids=[] + + if(table.rows('.active').data().length){ + $.each(table.rows('.active').data(),function(key, value){ + names+=value['username']+'\n'; + ids.push({'id':value['id'],'username':value['username']}); + }); + var text = "You are about to "+action+" these users:\n\n "+names + }else{ + $.each(table.rows({filter: 'applied'}).data(),function(key, value){ + ids.push({'id':value['id'],'username':value['username']}); + }); + var text = "You are about to "+action+" "+table.rows({filter: 'applied'}).data().length+" users!\n To all the users in list!" + } + console.log(ids) + new PNotify({ + title: 'Bulk actions on users', + text: text, + hide: false, + opacity: 0.9, + confirm: { + confirm: true + }, + buttons: { + closer: false, + sticker: false + }, + history: { + history: false + }, + addclass: 'pnotify-center' + }).get().on('pnotify.confirm', function() { + $.ajax({ + type: "PUT", + url:"/api/users_bulk/"+$('#bulk_actions').val(), + data: JSON.stringify(ids), + success: function(data) + { + console.log('SUCCESS') + $('#bulk_actions option[value=""]').prop("selected",true); + table.ajax.reload(); + }, + error: function(data) + { + alert('Something went wrong on our side...') + $('#bulk_actions option[value=""]').prop("selected",true); + table.ajax.reload(); + } + }); + }).on('pnotify.cancel', function() { + $('#bulk_actions option[value=""]').prop("selected",true); + }); + } ); + // Open new user modal $('.btn-new-user').on('click', function () { $("#modalAddUserForm")[0].reset(); @@ -281,6 +316,13 @@ $(document).ready(function() { \ ' }, + { + "className": 'text-center', + "data": null, + "orderable": false, + "defaultContent": '', + "width": "10px" + }, { "data": "username", "width": "10px"}, { "data": "first", "width": "10px"}, { "data": "last", "width": "150px"}, @@ -314,12 +356,12 @@ $(document).ready(function() { } }}, { - "targets": 4, + "targets": 5, "render": function ( data, type, full, meta ) { return ''+full.username+'' }}, { - "targets": 8, + "targets": 9, "render": function ( data, type, full, meta ) { grups = '' full.keycloak_groups.forEach(element => { @@ -328,7 +370,7 @@ $(document).ready(function() { return grups }}, { - "targets": 9, + "targets": 10, "render": function ( data, type, full, meta ) { if(full.quota == false){ return 'Unlimited' @@ -339,6 +381,15 @@ $(document).ready(function() { ] } ); + table.on( 'click', 'tr', function () { + $(this).toggleClass('active'); + if ($(this).hasClass('active')) { + $(this).find('input').prop('checked', true); + } else { + $(this).find('input').prop('checked', false); + } + } ); + // $template = $(".template-detail-users"); // $('#users').find('tbody').on('click', 'td.details-control', function () { @@ -406,6 +457,7 @@ $(document).ready(function() { error: function(data) { alert('Something went wrong on our side...') + table.ajax.reload(); } }); }).on('pnotify.cancel', function() { @@ -425,10 +477,12 @@ $(document).ready(function() { success: function(data) { $('#modalPasswdUserForm #password').val(data); + table.ajax.reload(); }, error: function(data) { alert('Something went wrong on our side...') + table.ajax.reload(); } }); break; @@ -455,6 +509,7 @@ $(document).ready(function() { error: function(data) { alert('Something went wrong on our side...') + table.ajax.reload(); } // statusCode: { // 404: function(data) { diff --git a/admin/src/admin/static/templates/pages/users.html b/admin/src/admin/static/templates/pages/users.html index 0278eab..112e5de 100644 --- a/admin/src/admin/static/templates/pages/users.html +++ b/admin/src/admin/static/templates/pages/users.html @@ -14,6 +14,19 @@

Users

diff --git a/admin/src/admin/views/ApiViews.py b/admin/src/admin/views/ApiViews.py index 8ddb7b0..4b3c7f6 100644 --- a/admin/src/admin/views/ApiViews.py +++ b/admin/src/admin/views/ApiViews.py @@ -16,7 +16,7 @@ from ..lib.helpers import system_group # import Queue import threading -threads={} +threads={'external':None} # q = Queue.Queue() from keycloak.exceptions import KeycloakGetError @@ -70,6 +70,51 @@ def users(provider=False): user['keycloak_groups'] = [g for g in user['keycloak_groups'] if not system_group(g) ] return json.dumps(users), 200, {'Content-Type': 'application/json'} +@app.route('/api/users_bulk/', methods=['PUT']) +@login_required +def users_bulk(action): + data=request.get_json(force=True) + if request.method == 'PUT': + if action == 'enable': + if 'external' in threads.keys(): + if threads['external'] is not None and threads['external'].is_alive(): + return json.dumps({'msg':'Precondition failed: already operating users'}), 412, {'Content-Type': 'application/json'} + else: + threads['external']=None + try: + threads['external'] = threading.Thread(target=app.admin.enable_users, args=(data,)) + threads['external'].start() + return json.dumps({}), 200, {'Content-Type': 'application/json'} + except: + log.error(traceback.format_exc()) + return json.dumps({'msg':'Enable users error.'}), 500, {'Content-Type': 'application/json'} + if action == 'disable': + if 'external' in threads.keys(): + if threads['external'] is not None and threads['external'].is_alive(): + return json.dumps({'msg':'Precondition failed: already operating users'}), 412, {'Content-Type': 'application/json'} + else: + threads['external']=None + try: + threads['external'] = threading.Thread(target=app.admin.disable_users, args=(data,)) + threads['external'].start() + return json.dumps({}), 200, {'Content-Type': 'application/json'} + except: + log.error(traceback.format_exc()) + return json.dumps({'msg':'Disabling users error.'}), 500, {'Content-Type': 'application/json'} + if action == 'delete': + if 'external' in threads.keys(): + if threads['external'] is not None and threads['external'].is_alive(): + return json.dumps({'msg':'Precondition failed: already operating users'}), 412, {'Content-Type': 'application/json'} + else: + threads['external']=None + try: + threads['external'] = threading.Thread(target=app.admin.delete_users, args=(data,)) + threads['external'].start() + return json.dumps({}), 200, {'Content-Type': 'application/json'} + except: + log.error(traceback.format_exc()) + return json.dumps({'msg':'Deleting users error.'}), 500, {'Content-Type': 'application/json'} + return json.dumps({}), 405, {'Content-Type': 'application/json'} # Update pwd @app.route('/api/user_password', methods=['GET']) @@ -219,11 +264,15 @@ def external(): @app.route('/api/external/users') @login_required def external_users_list(): + while threads['external'] is not None and threads['external'].is_alive(): + time.sleep(.5) return json.dumps(app.admin.get_external_users()), 200, {'Content-Type': 'application/json'} @app.route('/api/external/groups') @login_required def external_groups_list(): + while threads['external'] is not None and threads['external'].is_alive(): + time.sleep(.5) return json.dumps(app.admin.get_external_groups()), 200, {'Content-Type': 'application/json'} @app.route('/api/external/roles', methods=['PUT'])