Added bulk actions

root 2021-09-15 09:40:26 +02:00
parent 931809fe7c
commit b0eba9d7bb
6 changed files with 205 additions and 37 deletions

View File

@ -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
#######################

View File

@ -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)

View File

@ -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

View File

@ -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(
// '<option value=' + element.path + '>' + element.name + ' (' + groupOrigins.join(',') + ') </option>'
// )
// });
// $('.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() {
<button id="btn-password" class="btn btn-xs" type="button" data-placement="top" ><i class="fa fa-lock" style="color:orange"></i></button> \
<button id="btn-edit" class="btn btn-xs" type="button" data-placement="top" ><i class="fa fa-pencil" style="color:darkblue"></i></button>'
},
{
"className": 'text-center',
"data": null,
"orderable": false,
"defaultContent": '<input type="checkbox" class="form-check-input"></input>',
"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 '<b>'+full.username+'</b>'
}},
{
"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) {

View File

@ -14,6 +14,19 @@
<div class="x_title">
<h3><i class="fa fa-user"></i> Users</h3>
<ul class="nav navbar-right panel_toolbox">
<li>
<div class="item form-group">
<label class="control-label col-md-3 col-sm-3 col-xs-12" for="bulk_actions">Bulk actions: <span class="required">*</span></label>
<div class="col-md-6 col-sm-6 col-xs-12">
<select id="bulk_actions" name="bulk_actions" class="form-control action" required>
<option value=''>Select action</option>
<option value='enable'>Enable</option>
<option value='disable'>Disable</option>
<option value='delete'>Delete</option>
</select>
</div>
</div>
</li>
<li>
<a class="btn-new-user"><span style="color: #5499c7; "><i class="fa fa-plus"></i> Add new</span></a>
</li>
@ -28,6 +41,7 @@
<th>Avatar</th>
<th>Role</th>
<th>Actions</th>
<th>Selected</th>
<th>Username</th>
<th>First</th>
<th>Last</th>
@ -38,6 +52,21 @@
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<th>Enabled</th>
<th>Avatar</th>
<th>Role</th>
<th>Actions</th>
<th>Selected</th>
<th>Username</th>
<th>First</th>
<th>Last</th>
<th>Email</th>
<th>Groups</th>
<th>Quota</th>
</tr>
</tfoot>
</table>
</div>
</div>

View File

@ -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/<action>', 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'])