forked from shaba/openuds
Edit users done. I'll update the form, because now we can add all fields to 1 single tab
This commit is contained in:
parent
b3c6e46f0b
commit
db29667f4c
@ -63,6 +63,15 @@ class Authenticators(ModelHandler):
|
||||
def enum_types(self):
|
||||
return auths.factory().providers().values()
|
||||
|
||||
def typeInfo(self, type_):
|
||||
return {
|
||||
'canSearchUsers' : type_.searchUsers != auths.Authenticator.searchUsers,
|
||||
'canSearchGroups' : type_.searchGroups != auths.Authenticator.searchGroups,
|
||||
'needsPassword' : type_.needsPassword, 'userNameLabel' : _(type_.userNameLabel),
|
||||
'groupNameLabel' : _(type_.groupNameLabel), 'passwordLabel' : _(type_.passwordLabel),
|
||||
'canCreateUsers' : type_.createUser != auths.Authenticator.createUser,
|
||||
}
|
||||
|
||||
def getGui(self, type_):
|
||||
try:
|
||||
return self.addDefaultFields(auths.factory().lookup(type_).guiDescription(), ['name', 'comments', 'priority', 'small_name'])
|
||||
|
@ -35,11 +35,12 @@ from __future__ import unicode_literals
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.forms.models import model_to_dict
|
||||
from uds.core.util.State import State
|
||||
from django.db import IntegrityError
|
||||
|
||||
from uds.core.util import log
|
||||
from uds.models import Authenticator
|
||||
from uds.models import Authenticator, User, Group
|
||||
from uds.REST import RequestError
|
||||
|
||||
from uds.REST.handlers import HandlerError
|
||||
from uds.REST.model import DetailHandler
|
||||
|
||||
import logging
|
||||
@ -88,6 +89,38 @@ class Users(DetailHandler):
|
||||
|
||||
return log.getLogs(user)
|
||||
|
||||
def saveItem(self, parent, item):
|
||||
# Extract item db fields
|
||||
# We need this fields for all
|
||||
logger.debug('Saving user {0} / {1}'.format(parent, item))
|
||||
fields = self.readFieldsFromParams(['name', 'real_name', 'comments', 'state', 'staff_member', 'is_admin', 'groups'])
|
||||
try:
|
||||
auth = parent.getInstance()
|
||||
groups = fields['groups']
|
||||
del fields['groups'] # Not update this on user dict
|
||||
if item is None: # Create new
|
||||
auth.createUser(fields) # this throws an exception if there is an error (for example, this auth can't create users)
|
||||
user = parent.users.create(**fields)
|
||||
else:
|
||||
auth.modifyUser(fields) # Notifies authenticator
|
||||
user = parent.users.get(pk=item)
|
||||
user.__dict__.update(fields)
|
||||
|
||||
if auth.isExternalSource == False and user.parent == -1:
|
||||
user.groups = Group.objects.filter(id__in=groups)
|
||||
|
||||
user.save()
|
||||
|
||||
except User.DoesNotExist:
|
||||
self.invalidItemException()
|
||||
except IntegrityError: # Duplicate key probably
|
||||
raise RequestError(_('User already exists (duplicate key error)'))
|
||||
except Exception:
|
||||
logger.exception('Saving Service')
|
||||
self.invalidRequestException()
|
||||
|
||||
return self.getItems(parent, user.id)
|
||||
|
||||
class Groups(DetailHandler):
|
||||
|
||||
def getItems(self, parent, item):
|
||||
|
@ -111,12 +111,17 @@ class BaseModelHandler(Handler):
|
||||
|
||||
return gui
|
||||
|
||||
def typeInfo(self, type_):
|
||||
return {}
|
||||
|
||||
def type_as_dict(self, type_):
|
||||
return { 'name' : _(type_.name()),
|
||||
res = self.typeInfo(type_)
|
||||
res.update( { 'name' : _(type_.name()),
|
||||
'type' : type_.type(),
|
||||
'description' : _(type_.description()),
|
||||
'icon' : type_.icon().replace('\n', '')
|
||||
}
|
||||
})
|
||||
return res
|
||||
|
||||
def processTableFields(self, title, fields):
|
||||
# processedFields = [{ 'id' : {'visible': False, 'sortable': False, 'searchable': False } }]
|
||||
|
@ -17,6 +17,18 @@
|
||||
return options.inverse(this);
|
||||
}
|
||||
});
|
||||
|
||||
// Belongs comparision (similar to "if xxx in yyyyy")
|
||||
// Use as block as {{#ifbelong [element] [group]}}....{{/ifbelongs}}
|
||||
Handlebars.registerHelper('ifbelongs', function(context1, context2, options) {
|
||||
gui.doLog('belongs', context1, context2);
|
||||
if($.inArray(context1, context2) != -1) {
|
||||
gui.doLog('belongs is true');
|
||||
return options.fn(this);
|
||||
} else {
|
||||
return options.inverse(this);
|
||||
}
|
||||
});
|
||||
|
||||
// Counters.
|
||||
// Create a counter with {{counter [id] [startValue]}}
|
||||
@ -61,7 +73,7 @@
|
||||
success_fnc = success_fnc || function(){};
|
||||
api.doLog('Getting template ' + name);
|
||||
if (name.indexOf('?') == -1) {
|
||||
if ($this.cache.get(name) ) {
|
||||
if ($this.cache.get(name+'-------') ) {
|
||||
success_fnc($this.cache.get(name));
|
||||
return;
|
||||
// Let's check if a "preloaded template" exists
|
||||
|
@ -127,7 +127,7 @@
|
||||
|
||||
|
||||
// Public attributes
|
||||
api.debug = false;
|
||||
api.debug = true;
|
||||
}(window.api = window.api || {}, jQuery));
|
||||
|
||||
|
||||
|
@ -12,6 +12,8 @@ gui.authenticators.link = function(event) {
|
||||
},
|
||||
};
|
||||
|
||||
// Clears the log of the detail, in this case, the log of "users"
|
||||
// Memory saver :-)
|
||||
var detailLogTable;
|
||||
var clearDetailLog = function() {
|
||||
if( detailLogTable ) {
|
||||
@ -23,6 +25,8 @@ gui.authenticators.link = function(event) {
|
||||
}
|
||||
};
|
||||
|
||||
// Clears the details
|
||||
// Memory saver :-)
|
||||
var prevTables = [];
|
||||
var clearDetails = function() {
|
||||
$.each(prevTables, function(undefined, tbl){
|
||||
@ -42,7 +46,6 @@ gui.authenticators.link = function(event) {
|
||||
prevTables = [];
|
||||
};
|
||||
|
||||
gui.doLog('enter auths');
|
||||
api.templates.get('authenticators', function(tmpl) {
|
||||
gui.clearWorkspace();
|
||||
gui.appendToWorkspace(api.templates.evaluate(tmpl, {
|
||||
@ -71,6 +74,9 @@ gui.authenticators.link = function(event) {
|
||||
|
||||
gui.tools.blockUI();
|
||||
var id = selected[0].id;
|
||||
var type = gui.authenticators.types[selected[0].type];
|
||||
gui.doLog('Type', type);
|
||||
|
||||
var user = new GuiElement(api.authenticators.detail(id, 'users'), 'users');
|
||||
var group = new GuiElement(api.authenticators.detail(id, 'groups'), 'groups');
|
||||
var grpTable = group.table({
|
||||
@ -82,7 +88,13 @@ gui.authenticators.link = function(event) {
|
||||
},
|
||||
});
|
||||
var tmpLogTable;
|
||||
// Use defered rendering for users, this table can be "huge"
|
||||
|
||||
// New button will only be shown on authenticators that can create new users
|
||||
var usrButtons = ['edit', 'delete', 'xls'];
|
||||
if( type.canCreateUsers ) {
|
||||
usrButtons = ['new'].concat(usrButtons); // New is first button
|
||||
}
|
||||
|
||||
var usrTable = user.table({
|
||||
container : 'users-placeholder',
|
||||
rowSelect : 'single',
|
||||
@ -103,12 +115,64 @@ gui.authenticators.link = function(event) {
|
||||
onRowDeselect : function() {
|
||||
clearDetailLog();
|
||||
},
|
||||
buttons : [ 'new', 'edit', 'delete', 'xls' ],
|
||||
deferedRender: true,
|
||||
buttons : usrButtons,
|
||||
deferedRender: true, // Use defered rendering for users, this table can be "huge"
|
||||
scrollToTable : false,
|
||||
onLoad: function(k) {
|
||||
gui.tools.unblockUI();
|
||||
},
|
||||
onEdit: function(value, event, table, refreshFnc) {
|
||||
var password = "#æð~¬~@æß”¢ß€~½¬@#~½¬@|"; // Garbage for password (to detect change)
|
||||
// Gets fields gui
|
||||
gui.tools.blockUI();
|
||||
api.templates.get('user', function(tmpl) { // Get form template
|
||||
group.rest.overview(function(groups) { // Get groups
|
||||
user.rest.item(value.id, function(item){ // Get item to edit
|
||||
|
||||
// Creates modal
|
||||
var modalId = gui.launchModal(gettext('Edit user'), api.templates.evaluate(tmpl, {
|
||||
id: item.id,
|
||||
username: item.name,
|
||||
username_label: type.userNameLabel,
|
||||
realname: item.real_name,
|
||||
comments: item.comments,
|
||||
state: item.state,
|
||||
staff_member: item.staff_member,
|
||||
is_admin: item.is_admin,
|
||||
password: type.needsPassword ? password : undefined,
|
||||
password_label: type.passwordLabel,
|
||||
groups_all: groups,
|
||||
groups: item.groups,
|
||||
}));
|
||||
|
||||
// Activate "custom" styles
|
||||
$(modalId + ' .make-switch').bootstrapSwitch();
|
||||
// Activate "cool" selects
|
||||
$(modalId + ' .selectpicker').selectpicker();
|
||||
// TEST: cooler on mobile devices
|
||||
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) ) {
|
||||
$(modalId + ' .selectpicker').selectpicker('mobile');
|
||||
}
|
||||
// Activate tooltips
|
||||
$(modalId + ' [data-toggle="tooltip"]').tooltip({delay: {show: 1000, hide: 100}, placement: 'auto right'});
|
||||
|
||||
gui.tools.unblockUI();
|
||||
|
||||
$(modalId + ' .button-accept').click(function(){
|
||||
var fields = gui.forms.read(modalId);
|
||||
gui.doLog('Fields', fields);
|
||||
user.rest.save(fields, function(data) { // Success on put
|
||||
$(modalId).modal('hide');
|
||||
refreshFnc();
|
||||
gui.notify(gettext('User saved'), 'success');
|
||||
}, gui.failRequestModalFnc("Error saving user", true));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
onNew: function(type, table, refreshFnc) {
|
||||
}
|
||||
});
|
||||
|
||||
var logTable = gui.authenticators.logTable(id, {
|
||||
|
@ -21,28 +21,25 @@ GuiElement.prototype = {
|
||||
gui.doLog('Initializing ' + this.name);
|
||||
var self = this;
|
||||
this.rest.types(function(data) {
|
||||
var styles = '';
|
||||
var alreadyAttached = $('#gui-style-'+self.name).length !== 0;
|
||||
$.each(data, function(index, value) {
|
||||
var className = self.name + '-' + value.type;
|
||||
self.types[value.type] = {
|
||||
css : className,
|
||||
name : value.name || '',
|
||||
description : value.description || ''
|
||||
};
|
||||
gui.doLog('Creating style for ' + className);
|
||||
if( !alreadyAttached ) {
|
||||
var style = '.' + className + ' { display:inline-block; background: url(data:image/png;base64,' +
|
||||
value.icon + '); ' + 'width: 16px; height: 16px; vertical-align: middle; } ';
|
||||
styles += style;
|
||||
}
|
||||
});
|
||||
if (styles !== '') {
|
||||
// If style already attached, do not re-attach it
|
||||
styles = '<style id="gui-style-' + self.name + '" media="screen">' + styles + '</style>';
|
||||
$(styles).appendTo('head');
|
||||
var styles = '';
|
||||
var alreadyAttached = $('#gui-style-'+self.name).length !== 0;
|
||||
$.each(data, function(index, value) {
|
||||
var className = self.name + '-' + value.type;
|
||||
self.types[value.type] = value;
|
||||
self.types[value.type].css = className;
|
||||
gui.doLog('Creating style for ' + className);
|
||||
if( !alreadyAttached ) {
|
||||
var style = '.' + className + ' { display:inline-block; background: url(data:image/png;base64,' +
|
||||
value.icon + '); ' + 'width: 16px; height: 16px; vertical-align: middle; } ';
|
||||
styles += style;
|
||||
}
|
||||
});
|
||||
if (styles !== '') {
|
||||
// If style already attached, do not re-attach it
|
||||
styles = '<style id="gui-style-' + self.name + '" media="screen">' + styles + '</style>';
|
||||
$(styles).appendTo('head');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Options: dictionary
|
||||
|
@ -24,6 +24,9 @@
|
||||
var originalValues = {}; // Initial stored values (defaults to "reset" form and also used on fillers callback to try to restore previous value)
|
||||
// itemGui is expected to have fields sorted by .gui.order (REST api returns them sorted)
|
||||
$.each(itemGui, function(index, f){
|
||||
if( f.gui === undefined ) { // Not exactly a field, maybe some other info...
|
||||
return;
|
||||
}
|
||||
// Fix multiline text fields to textbox
|
||||
if( f.gui.type == 'text' && f.gui.multiline ) {
|
||||
f.gui.type = 'textbox';
|
||||
|
@ -1,11 +1,11 @@
|
||||
{% extends "uds/admin/tmpl/fld/form-group.html" %}
|
||||
{% load i18n %}
|
||||
{% comment %}The choice item MUST be a Select{% endcomment %}
|
||||
{% comment %}The choice item MUST be a Select.{% endcomment %}
|
||||
{% block field %}
|
||||
{% verbatim %}
|
||||
<select class="selectpicker show-menu-arrow {{ css }}" multiple data-style="btn-default" data-selected-text-format="count>3" data-width="100%" name="{{ name }}" id="{{ name }}_field" {{# if readonly }} disabled{{/ if }}>
|
||||
{{#each values }}
|
||||
<option value="{{ id }}"{{# ifequals id ../value }}selected{{/ ifequals }}>{{ text }}</option>
|
||||
<option value="{{ id }}"{{# ifbelongs id ../value }}selected{{/ ifbelongs }}>{{ text }}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
{% endverbatim %}
|
||||
|
93
server/src/uds/templates/uds/admin/tmpl/user.html
Normal file
93
server/src/uds/templates/uds/admin/tmpl/user.html
Normal file
@ -0,0 +1,93 @@
|
||||
{% load i18n %}
|
||||
{% verbatim %}
|
||||
<form id="user_form" class="form-horizontal" role="form">
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#id_user_pane" data-toggle="tab">{% endverbatim %}{% trans 'User' %}{% verbatim %}</a></li>
|
||||
<li><a href="#id_groups_pane" data-toggle="tab">{% endverbatim %}{% trans 'Groups' %}{% verbatim %}</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active fade in" id="id_user_pane">
|
||||
|
||||
<input type="hidden" name="id" value="{{ id }}" class="modal_field_data">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_username" class="col-sm-2 control-label">{{ username_label }}</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="name" value="{{ username }}" type="text" id="id_username" class="form-control modal_field_data"
|
||||
placeholder="{% endverbatim %}{% trans 'Username' %}{% verbatim %}"{{# if username }}readonly{{/ if }}>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_realname" class="col-sm-2 control-label">{% endverbatim %}{% trans 'Name' %}{% verbatim %}</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="real_name" value="{{ realname }}" type="text" id="id_realname" class="form-control modal_field_data"
|
||||
placeholder="{% endverbatim %}{% trans 'Name' %}{% verbatim %}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_comments" class="col-sm-2 control-label">{% endverbatim %}{% trans 'comments' %}{% verbatim %}</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="comments" value="{{ comments }}" type="text" id="id_comments" class="form-control modal_field_data"
|
||||
placeholder="{% endverbatim %}{% trans 'Comments' %}{% verbatim %}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_state" class="col-sm-2 control-label">{% endverbatim %}{% trans 'State' %}{% verbatim %}</label>
|
||||
<div class="col-sm-10">
|
||||
<select name="state" class="selectpicker show-menu-arrow show-tick modal_field_data" data-style="btn-default" data-width="100%" id="id_state">
|
||||
<option value="A"{{# ifequals state 'A'}} selected{{/ ifequals }}>{% endverbatim %}{% trans 'Enabled' %}{% verbatim %}</option>
|
||||
<option value="I"{{# ifequals state 'I'}} selected{{/ ifequals }}>{% endverbatim %}{% trans 'Disabled' %}{% verbatim %}</option>
|
||||
<option value="B"{{# ifequals state 'B'}} selected{{/ ifequals }}>{% endverbatim %}{% trans 'Blocked' %}{% verbatim %}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_staffmember" class="col-sm-2 control-label">{% endverbatim %}{% trans 'Staff member' %}{% verbatim %}</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="make-switch" data-on-label="{% endverbatim %}{% trans 'Yes' %}{% verbatim %}"
|
||||
data-off-label="{% endverbatim %}{% trans 'No' %}{% verbatim %}">
|
||||
<input type="checkbox" class="modal_field_data" name="staff_member" id="id_staffmember"{{# if staff_member }} checked{{/ if }}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_admin" class="col-sm-2 control-label">{% endverbatim %}{% trans 'Admin' %}{% verbatim %}</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="make-switch" data-on-label="{% endverbatim %}{% trans 'Yes' %}{% verbatim %}" data-off-label="{% endverbatim %}{% trans 'No' %}{% verbatim %}">
|
||||
<input type="checkbox" class="modal_field_data" name="is_admin" id="id_admin"{{# if is_admin }} checked{{/ if }}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{# if password }}
|
||||
<div class="form-group">
|
||||
<label for="id_password" class="col-sm-2 control-label">{{ password_label }}</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="password" value="{{ password }}" type="password" id="id_password" class="form-control modal_field_data"
|
||||
placeholder="{% endverbatim %}{% trans 'Password' %}{% verbatim %}">
|
||||
</div>
|
||||
</div>
|
||||
{{/ if }}
|
||||
|
||||
</div>
|
||||
<div class="tab-pane fade" id="id_groups_pane">
|
||||
<div class="form-group">
|
||||
<label for="id_password" class="col-sm-2 control-label">{% endverbatim %}{% trans 'Groups' %}{% verbatim %}</label>
|
||||
<div class="col-sm-10">
|
||||
<select class="selectpicker show-menu-arrow modal_field_data" multiple data-style="btn-default" countSelectedText="{% endverbatim %}{% trans '{0} of {1} selected' %}{% verbatim %}"
|
||||
data-selected-text-format="count>3" data-width="100%" name="groups" id="id_groups">
|
||||
{{#each groups_all }}
|
||||
<option value="{{ id }}"{{# ifbelongs id ../groups }}selected{{/ ifbelongs }}>{{ name }}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endverbatim %}
|
@ -50,7 +50,6 @@ def dictFromUser(usr, groups = None):
|
||||
'staffMember' : usr.staff_member, 'isAdmin' : usr.is_admin }
|
||||
if groups != None:
|
||||
dct['groups'] = groups
|
||||
logger.debug('Dict: {0}'.format(dct))
|
||||
return dct
|
||||
|
||||
@needs_credentials
|
||||
|
Loading…
Reference in New Issue
Block a user