mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Enable multi-value field support for some attributes on the edit pages
Better error reporting in the GUI Include a document describing how multi-valued fields work
This commit is contained in:
@@ -28,6 +28,11 @@ class IPAError(exceptions.Exception):
|
||||
error."""
|
||||
self.code = code
|
||||
self.message = message
|
||||
# Fill this in as an empty LDAP error message so we don't have a lot
|
||||
# of "if e.detail ..." everywhere
|
||||
if detail is None:
|
||||
detail = []
|
||||
detail.append({'desc':'','info':''})
|
||||
self.detail = detail
|
||||
|
||||
def __str__(self):
|
||||
|
||||
27
ipa-server/ipa-gui/README.multivalue
Normal file
27
ipa-server/ipa-gui/README.multivalue
Normal file
@@ -0,0 +1,27 @@
|
||||
The way multi-valued fields work is this:
|
||||
- A new widget is added to the form. I name it as the attribute + s.
|
||||
For example, I use cns for the cn attribute.
|
||||
- If you need a new validator use a ForEach() so that each value is
|
||||
checked.
|
||||
- This attribute is populated from the incoming attribute from the
|
||||
user or group record. The widget can support multiple fields at once
|
||||
but I'm using it for just one field. In fact, I don't know if it
|
||||
will work with more the way I'm using it.
|
||||
- In the GUI an operator can add/remove values to each multi-valued field.
|
||||
- Naming is very important in the widget. TurboGears automatically
|
||||
re-assembles the data into a list of dict entries if you name things
|
||||
properly. For example, the cns (multiple CN entries) looks like:
|
||||
cns-0.cn=Rob+Crittenden&cns-1.cn=Robert+Crittenden&cns-2.cn=rcrit
|
||||
- This gets converted to:
|
||||
[{'cn': u'Rob Crittenden'}, {'cn': u'Robert Crittenden'}, {'cn': u'rcrit'}]
|
||||
- I take this list of dicts and pull out each value and append it to a new
|
||||
list that represents the original multi-valued field
|
||||
- Then the list/dict version is removed (in this case, kw['cns']).
|
||||
|
||||
When adding a new field you have to update:
|
||||
|
||||
1. The form to add the new ExpandingForm() field and perhaps a validator
|
||||
2. The edit template to add the boilerplate to display the field
|
||||
3. The show template to be able to display all the fields separately
|
||||
4. The new template if you want to be able to enter these on new entries
|
||||
5. The subcontroller so you can do the input and output conversions
|
||||
@@ -1,8 +1,10 @@
|
||||
import turbogears
|
||||
from turbogears import validators, widgets
|
||||
from tg_expanding_form_widget.tg_expanding_form_widget import ExpandingForm
|
||||
|
||||
class GroupFields():
|
||||
cn = widgets.TextField(name="cn", label="Name")
|
||||
cns = ExpandingForm(name="cns", label="Common Names", fields=[cn])
|
||||
gidnumber = widgets.TextField(name="gidnumber", label="GID")
|
||||
description = widgets.TextField(name="description", label="Description")
|
||||
|
||||
@@ -37,6 +39,7 @@ class GroupNewForm(widgets.Form):
|
||||
|
||||
|
||||
class GroupEditValidator(validators.Schema):
|
||||
cn = validators.ForEach(validators.String(not_empty=True))
|
||||
gidnumber = validators.Int(not_empty=False)
|
||||
description = validators.String(not_empty=False)
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import turbogears
|
||||
from turbogears import validators, widgets
|
||||
from tg_expanding_form_widget.tg_expanding_form_widget import ExpandingForm
|
||||
|
||||
class UserFields():
|
||||
givenname = widgets.TextField(name="givenname", label="Given Name")
|
||||
sn = widgets.TextField(name="sn", label="Family Name")
|
||||
cn = widgets.TextField(name="cn", label="Common Names")
|
||||
cns = ExpandingForm(name="cns", label="Common Names", fields=[cn])
|
||||
title = widgets.TextField(name="title", label="Title")
|
||||
displayname = widgets.TextField(name="displayname", label="Display Name")
|
||||
initials = widgets.TextField(name="initials", label="Initials")
|
||||
@@ -21,11 +23,16 @@ class UserFields():
|
||||
|
||||
mail = widgets.TextField(name="mail", label="E-mail Address")
|
||||
telephonenumber = widgets.TextField(name="telephonenumber", label="Work Number")
|
||||
telephonenumbers = ExpandingForm(name="telephonenumbers", label="Work Numbers", fields=[telephonenumber])
|
||||
facsimiletelephonenumber = widgets.TextField(name="facsimiletelephonenumber",
|
||||
label="Fax Number")
|
||||
facsimiletelephonenumbers = ExpandingForm(name="facsimiletelephonenumbers", label="Fax Numbers", fields=[facsimiletelephonenumber])
|
||||
mobile = widgets.TextField(name="mobile", label="Cell Number")
|
||||
mobiles = ExpandingForm(name="mobiles", label="Cell Numbers", fields=[mobile])
|
||||
pager = widgets.TextField(name="pager", label="Pager Number")
|
||||
pagers = ExpandingForm(name="pagers", label="Pager Numbers", fields=[pager])
|
||||
homephone = widgets.TextField(name="homephone", label="Home Number")
|
||||
homephones = ExpandingForm(name="homephones", label="Home Numbers", fields=[homephone])
|
||||
|
||||
street = widgets.TextField(name="street", label="Street Address")
|
||||
l = widgets.TextField(name="l", label="City")
|
||||
@@ -102,6 +109,7 @@ class UserEditValidator(validators.Schema):
|
||||
userpassword_confirm = validators.String(not_empty=False)
|
||||
givenname = validators.String(not_empty=True)
|
||||
sn = validators.String(not_empty=True)
|
||||
cn = validators.ForEach(validators.String(not_empty=True))
|
||||
mail = validators.Email(not_empty=True)
|
||||
uidnumber = validators.Int(not_empty=False)
|
||||
gidnumber = validators.Int(not_empty=False)
|
||||
|
||||
@@ -90,7 +90,7 @@ class GroupController(IPAController):
|
||||
# on any error, we redirect to the _edit_ group page.
|
||||
# this code does data setup, similar to groupedit()
|
||||
#
|
||||
group = client.get_entry_by_cn(kw['cn'], group_fields)
|
||||
group = client.get_entry_by_cn(kw['cn'][0], group_fields)
|
||||
group_dict = group.toDict()
|
||||
member_dicts = []
|
||||
|
||||
@@ -180,6 +180,14 @@ class GroupController(IPAController):
|
||||
|
||||
group_dict = group.toDict()
|
||||
|
||||
# Load potential multi-valued fields
|
||||
if isinstance(group_dict['cn'], str):
|
||||
group_dict['cn'] = [group_dict['cn']]
|
||||
cns = []
|
||||
for cn in group_dict['cn']:
|
||||
cns.append(dict(cn=cn))
|
||||
group_dict['cns'] = cns
|
||||
|
||||
#
|
||||
# convert members to users, for easier manipulation on the page
|
||||
#
|
||||
@@ -210,14 +218,19 @@ class GroupController(IPAController):
|
||||
self.restrict_post()
|
||||
client = self.get_ipaclient()
|
||||
|
||||
# Fix incoming multi-valued form fields
|
||||
kw['cn'] = []
|
||||
for i in range(len(kw['cns'])):
|
||||
kw['cn'].append(kw['cns'][i]['cn'])
|
||||
del(kw['cns'])
|
||||
|
||||
if kw.get('submit') == 'Cancel Edit':
|
||||
turbogears.flash("Edit group cancelled")
|
||||
raise turbogears.redirect('/group/show', cn=kw.get('cn'))
|
||||
raise turbogears.redirect('/group/show', cn=kw.get('cn')[0])
|
||||
|
||||
# Decode the member data, in case we need to round trip
|
||||
member_dicts = loads(b64decode(kw.get('member_data')))
|
||||
|
||||
|
||||
tg_errors, kw = self.groupupdatevalidate(**kw)
|
||||
if tg_errors:
|
||||
turbogears.flash("There were validation errors.<br/>" +
|
||||
@@ -233,6 +246,9 @@ class GroupController(IPAController):
|
||||
try:
|
||||
orig_group_dict = loads(b64decode(kw.get('group_orig')))
|
||||
|
||||
# remove multi-valued form fields
|
||||
del(orig_group_dict['cns'])
|
||||
|
||||
new_group = ipa.group.Group(orig_group_dict)
|
||||
if new_group.description != kw.get('description'):
|
||||
group_modified = True
|
||||
@@ -243,6 +259,14 @@ class GroupController(IPAController):
|
||||
group_modified = True
|
||||
new_group.setValue('gidnumber', new_gid)
|
||||
|
||||
# Did any cn entries change?
|
||||
oldcn = new_group.getValues('cn')
|
||||
if isinstance(oldcn, str):
|
||||
oldcn = [oldcn]
|
||||
if oldcn != kw['cn']:
|
||||
group_modified = True
|
||||
new_group.setValue('cn', kw['cn'])
|
||||
|
||||
if group_modified:
|
||||
rv = client.update_group(new_group)
|
||||
#
|
||||
@@ -252,7 +276,7 @@ class GroupController(IPAController):
|
||||
#
|
||||
kw['group_orig'] = b64encode(dumps(new_group.toDict()))
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("Group update failed: " + str(e))
|
||||
turbogears.flash("Group update failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
return dict(form=group_edit_form, group=kw, members=member_dicts,
|
||||
tg_template='ipagui.templates.groupedit')
|
||||
|
||||
@@ -268,8 +292,9 @@ class GroupController(IPAController):
|
||||
failed_adds = client.add_members_to_group(
|
||||
utf8_encode_values(dnadds), new_group.dn)
|
||||
kw['dnadd'] = failed_adds
|
||||
group_modified = True
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("Group update failed: " + str(e))
|
||||
turbogears.flash("Group update failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
return dict(form=group_edit_form, group=kw, members=member_dicts,
|
||||
tg_template='ipagui.templates.groupedit')
|
||||
|
||||
@@ -285,8 +310,9 @@ class GroupController(IPAController):
|
||||
failed_dels = client.remove_members_from_group(
|
||||
utf8_encode_values(dndels), new_group.dn)
|
||||
kw['dndel'] = failed_dels
|
||||
group_modified = True
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("Group update failed: " + str(e))
|
||||
turbogears.flash("Group update failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
return dict(form=group_edit_form, group=kw, members=member_dicts,
|
||||
tg_template='ipagui.templates.groupedit')
|
||||
|
||||
@@ -308,8 +334,11 @@ class GroupController(IPAController):
|
||||
return dict(form=group_edit_form, group=kw, members=member_dicts,
|
||||
tg_template='ipagui.templates.groupedit')
|
||||
|
||||
turbogears.flash("%s updated!" % kw['cn'])
|
||||
raise turbogears.redirect('/group/show', cn=kw['cn'])
|
||||
if group_modified == True:
|
||||
turbogears.flash("%s updated!" % kw['cn'][0])
|
||||
else:
|
||||
turbogears.flash("No modifications requested.")
|
||||
raise turbogears.redirect('/group/show', cn=kw['cn'][0])
|
||||
|
||||
|
||||
@expose("ipagui.templates.grouplist")
|
||||
@@ -330,7 +359,7 @@ class GroupController(IPAController):
|
||||
turbogears.flash("These results are truncated.<br />" +
|
||||
"Please refine your search and try again.")
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("Find groups failed: " + str(e))
|
||||
turbogears.flash("Find groups failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
raise turbogears.redirect("/group/list")
|
||||
|
||||
return dict(groups=groups, criteria=criteria,
|
||||
@@ -358,7 +387,7 @@ class GroupController(IPAController):
|
||||
return dict(group=group_dict, fields=ipagui.forms.group.GroupFields(),
|
||||
members = member_dicts)
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("Group show failed: " + str(e))
|
||||
turbogears.flash("Group show failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
raise turbogears.redirect("/")
|
||||
|
||||
@expose()
|
||||
|
||||
@@ -61,6 +61,35 @@ class UserController(IPAController):
|
||||
user_new_form.validator.add_field(s['field'], validator)
|
||||
user_edit_form.validator.add_field(s['field'], validator)
|
||||
|
||||
def setup_mv_fields(self, field, fieldname):
|
||||
"""Given a field (must be a list) and field name, convert that
|
||||
field into a list of dictionaries of the form:
|
||||
[ { fieldname : v1}, { fieldname : v2 }, .. ]
|
||||
|
||||
This is how we pre-fill values for multi-valued fields.
|
||||
"""
|
||||
mvlist = []
|
||||
if field is not None:
|
||||
for v in field:
|
||||
mvlist.append({ fieldname : v } )
|
||||
else:
|
||||
# We need to return an empty value so something can be
|
||||
# displayed on the edit page. Otherwise only an Add link
|
||||
# will show, not an empty field.
|
||||
mvlist.append({ fieldname : '' } )
|
||||
return mvlist
|
||||
|
||||
def fix_incoming_fields(self, fields, fieldname, multifieldname):
|
||||
"""This is called by the update() function. It takes the incoming
|
||||
list of dictionaries and converts it into back into the original
|
||||
field, then removes the multiple field.
|
||||
"""
|
||||
fields[fieldname] = []
|
||||
for i in range(len(fields[multifieldname])):
|
||||
fields[fieldname].append(fields[multifieldname][i][fieldname])
|
||||
del(fields[multifieldname])
|
||||
|
||||
return fields
|
||||
|
||||
@expose()
|
||||
def index(self):
|
||||
@@ -150,7 +179,7 @@ class UserController(IPAController):
|
||||
return dict(form=user_new_form, user=kw,
|
||||
tg_template='ipagui.templates.usernew')
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("User add failed: " + str(e))
|
||||
turbogears.flash("User add failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
return dict(form=user_new_form, user=kw,
|
||||
tg_template='ipagui.templates.usernew')
|
||||
|
||||
@@ -259,6 +288,32 @@ class UserController(IPAController):
|
||||
turbogears.flash("User edit failed: No uid or principal provided")
|
||||
raise turbogears.redirect('/')
|
||||
user_dict = user.toDict()
|
||||
|
||||
# Load potential multi-valued fields
|
||||
if isinstance(user_dict['cn'], str):
|
||||
user_dict['cn'] = [user_dict['cn']]
|
||||
user_dict['cns'] = self.setup_mv_fields(user_dict['cn'], 'cn')
|
||||
|
||||
if isinstance(user_dict.get('telephonenumber',''), str):
|
||||
user_dict['telephonenumber'] = [user_dict.get('telephonenumber'),'']
|
||||
user_dict['telephonenumbers'] = self.setup_mv_fields(user_dict.get('telephonenumber'), 'telephonenumber')
|
||||
|
||||
if isinstance(user_dict.get('facsimiletelephonenumber',''), str):
|
||||
user_dict['facsimiletelephonenumber'] = [user_dict.get('facsimiletelephonenumber'),'']
|
||||
user_dict['facsimiletelephonenumbers'] = self.setup_mv_fields(user_dict.get('facsimiletelephonenumber'), 'facsimiletelephonenumber')
|
||||
|
||||
if isinstance(user_dict.get('mobile',''), str):
|
||||
user_dict['mobile'] = [user_dict.get('mobile'),'']
|
||||
user_dict['mobiles'] = self.setup_mv_fields(user_dict.get('mobile'), 'mobile')
|
||||
|
||||
if isinstance(user_dict.get('pager',''), str):
|
||||
user_dict['pager'] = [user_dict.get('pager'),'']
|
||||
user_dict['pagers'] = self.setup_mv_fields(user_dict.get('pager'), 'pager')
|
||||
|
||||
if isinstance(user_dict.get('homephone',''), str):
|
||||
user_dict['homephone'] = [user_dict.get('homephone'),'']
|
||||
user_dict['homephones'] = self.setup_mv_fields(user_dict.get('homephone'), 'homephone')
|
||||
|
||||
# Edit shouldn't fill in the password field.
|
||||
if user_dict.has_key('userpassword'):
|
||||
del(user_dict['userpassword'])
|
||||
@@ -300,7 +355,7 @@ class UserController(IPAController):
|
||||
except ipaerror.IPAError, e:
|
||||
if uid is None:
|
||||
uid = principal
|
||||
turbogears.flash("User edit failed: " + str(e))
|
||||
turbogears.flash("User edit failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
raise turbogears.redirect('/user/show', uid=uid)
|
||||
|
||||
@expose()
|
||||
@@ -314,6 +369,14 @@ class UserController(IPAController):
|
||||
turbogears.flash("Edit user cancelled")
|
||||
raise turbogears.redirect('/user/show', uid=kw.get('uid'))
|
||||
|
||||
# Fix incoming multi-valued fields we created for the form
|
||||
kw = self.fix_incoming_fields(kw, 'cn', 'cns')
|
||||
kw = self.fix_incoming_fields(kw, 'telephonenumber', 'telephonenumbers')
|
||||
kw = self.fix_incoming_fields(kw, 'facsimiletelephonenumber', 'facsimiletelephonenumbers')
|
||||
kw = self.fix_incoming_fields(kw, 'mobile', 'mobiles')
|
||||
kw = self.fix_incoming_fields(kw, 'pager', 'pagers')
|
||||
kw = self.fix_incoming_fields(kw, 'homephone', 'homephones')
|
||||
|
||||
# Decode the group data, in case we need to round trip
|
||||
user_groups_dicts = loads(b64decode(kw.get('user_groups_data')))
|
||||
|
||||
@@ -334,6 +397,14 @@ class UserController(IPAController):
|
||||
try:
|
||||
orig_user_dict = loads(b64decode(kw.get('user_orig')))
|
||||
|
||||
# remove multi-valued fields we created for the form
|
||||
del(orig_user_dict['cns'])
|
||||
del(orig_user_dict['telephonenumbers'])
|
||||
del(orig_user_dict['facsimiletelephonenumbers'])
|
||||
del(orig_user_dict['mobiles'])
|
||||
del(orig_user_dict['pagers'])
|
||||
del(orig_user_dict['homephones'])
|
||||
|
||||
new_user = ipa.user.User(orig_user_dict)
|
||||
new_user.setValue('title', kw.get('title'))
|
||||
new_user.setValue('givenname', kw.get('givenname'))
|
||||
@@ -400,7 +471,7 @@ class UserController(IPAController):
|
||||
# too much work to figure out unless someone really screams
|
||||
pass
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("User update failed: " + str(e))
|
||||
turbogears.flash("User update failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
return dict(form=user_edit_form, user=kw,
|
||||
user_groups=user_groups_dicts,
|
||||
tg_template='ipagui.templates.useredit')
|
||||
@@ -412,7 +483,7 @@ class UserController(IPAController):
|
||||
if password_change:
|
||||
rv = client.modifyPassword(kw['krbprincipalname'], "", kw.get('userpassword'))
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("User password change failed: " + str(e))
|
||||
turbogears.flash("User password change failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
return dict(form=user_edit_form, user=kw,
|
||||
user_groups=user_groups_dicts,
|
||||
tg_template='ipagui.templates.useredit')
|
||||
@@ -481,7 +552,7 @@ class UserController(IPAController):
|
||||
turbogears.flash("These results are truncated.<br />" +
|
||||
"Please refine your search and try again.")
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("User list failed: " + str(e))
|
||||
turbogears.flash("User list failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
raise turbogears.redirect("/user/list")
|
||||
|
||||
return dict(users=users, uid=uid, fields=ipagui.forms.user.UserFields())
|
||||
@@ -523,7 +594,7 @@ class UserController(IPAController):
|
||||
user_groups=user_groups, user_reports=user_reports,
|
||||
user_manager=user_manager, user_secretary=user_secretary)
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("User show failed: " + str(e))
|
||||
turbogears.flash("User show failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
raise turbogears.redirect("/")
|
||||
|
||||
@expose()
|
||||
@@ -539,7 +610,7 @@ class UserController(IPAController):
|
||||
turbogears.flash("user deleted")
|
||||
raise turbogears.redirect('/user/list')
|
||||
except (SyntaxError, ipaerror.IPAError), e:
|
||||
turbogears.flash("User deletion failed: " + str(e))
|
||||
turbogears.flash("User deletion failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
raise turbogears.redirect('/user/list')
|
||||
|
||||
@validate(form=user_new_form)
|
||||
@@ -661,7 +732,7 @@ class UserController(IPAController):
|
||||
users_counter = users[0]
|
||||
users = users[1:]
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("search failed: " + str(e))
|
||||
turbogears.flash("search failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
|
||||
return dict(users=users, criteria=criteria,
|
||||
which_select=kw.get('which_select'),
|
||||
|
||||
@@ -25,6 +25,8 @@ from ipagui.helpers import ipahelper
|
||||
|
||||
<script type="text/javascript" charset="utf-8"
|
||||
src="${tg.url('/static/javascript/dynamicedit.js')}"></script>
|
||||
<script type="text/javascript" charset="utf-8"
|
||||
src="${tg.url('/tg_widgets/tg_expanding_form_widget/javascript/expanding_form.js')}"></script>
|
||||
|
||||
<?python searchurl = tg.url('/group/edit_search') ?>
|
||||
|
||||
@@ -66,15 +68,35 @@ from ipagui.helpers import ipahelper
|
||||
<table class="formtable" cellpadding="2" cellspacing="0" border="0">
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" for="${group_fields.cn.field_id}"
|
||||
<label class="fieldlabel" for="${group_fields.cns.field_id}"
|
||||
py:content="group_fields.cn.label" />:
|
||||
</th>
|
||||
<td>
|
||||
<!-- <span py:replace="group_fields.cn.display(value_for(group_fields.cn))" />
|
||||
<span py:if="tg.errors.get('cn')" class="fielderror"
|
||||
py:content="tg.errors.get('cn')" /> -->
|
||||
${value_for(group_fields.cn)}
|
||||
<td colspan="3">
|
||||
<table class="formtable" cellpadding="2" cellspacing="0" border="0" id="${group_fields.cns.field_id}">
|
||||
<tbody>
|
||||
<?python repetition = 0
|
||||
cn_index = 0
|
||||
cn_error = tg.errors.get('cn')
|
||||
?>
|
||||
<tr py:for="cn in value_for(group_fields.cn)"
|
||||
id="${group_fields.cns.field_id}_${repetition}"
|
||||
class="${group_fields.cns.field_class}">
|
||||
|
||||
<td py:for="field in group_fields.cns.fields">
|
||||
<span><input class="textfield" type="text" id="${group_fields.cns.field_id}_${repetition}_cn" name="cns-${repetition}.cn" value="${cn}"/></span>
|
||||
<span py:if="cn_error and cn_error[cn_index]" class="fielderror"
|
||||
py:content="tg.errors.get('cn')" />
|
||||
</td>
|
||||
<?python cn_index = cn_index + 1 ?>
|
||||
<td>
|
||||
<a
|
||||
href="javascript:ExpandingForm.removeItem('${group_fields.cns.field_id}_${repetition}')">Remove (-)</a>
|
||||
</td>
|
||||
<?python repetition = repetition + 1?>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a id="${group_fields.cns.field_id}_doclink" href="javascript:ExpandingForm.addItem('${group_fields.cns.field_id}');">Add ( + )</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<?python
|
||||
edit_url = tg.url('/group/edit', cn=group.get('cn'))
|
||||
edit_url = tg.url('/group/edit', cn=group.get('cn')[0])
|
||||
?>
|
||||
<div id="details">
|
||||
<h1>View Group</h1>
|
||||
@@ -22,7 +22,21 @@ edit_url = tg.url('/group/edit', cn=group.get('cn'))
|
||||
<th>
|
||||
<label class="fieldlabel" py:content="fields.cn.label" />:
|
||||
</th>
|
||||
<td>${group.get("cn")}</td>
|
||||
<td>
|
||||
<table cellpadding="2" cellspacing="0" border="0">
|
||||
<tbody>
|
||||
<?python
|
||||
index = 0
|
||||
values = group.get("cn")
|
||||
if isinstance(values, str):
|
||||
values = [values]
|
||||
?>
|
||||
<tr py:for="index in range(len(values))">
|
||||
<td>${values[index]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
@@ -51,7 +65,7 @@ edit_url = tg.url('/group/edit', cn=group.get('cn'))
|
||||
member_type = "user"
|
||||
view_url = tg.url('/user/show', uid=member_uid)
|
||||
else:
|
||||
member_cn = "%s" % member.get('cn')
|
||||
member_cn = "%s" % member.get('cn')[0]
|
||||
member_desc = "[group]"
|
||||
member_type = "group"
|
||||
view_url = tg.url('/group/show', cn=member_cn)
|
||||
|
||||
@@ -26,6 +26,8 @@ from ipagui.helpers import ipahelper
|
||||
src="${tg.url('/static/javascript/dynamicedit.js')}"></script>
|
||||
<script type="text/javascript" charset="utf-8"
|
||||
src="${tg.url('/static/javascript/dynamicselect.js')}"></script>
|
||||
<script type="text/javascript" charset="utf-8"
|
||||
src="${tg.url('/tg_widgets/tg_expanding_form_widget/javascript/expanding_form.js')}"></script>
|
||||
|
||||
<?python
|
||||
searchurl = tg.url('/user/edit_search')
|
||||
@@ -141,14 +143,35 @@ from ipagui.helpers import ipahelper
|
||||
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" for="${user_fields.cn.field_id}"
|
||||
py:content="user_fields.cn.label" />:
|
||||
<label class="fieldlabel" for="${user_fields.cns.field_id}"
|
||||
py:content="user_fields.cns.label" />:
|
||||
</th>
|
||||
<td>
|
||||
<span py:replace="user_fields.cn.display(value_for(user_fields.cn))" />
|
||||
<span py:if="tg.errors.get('cn')" class="fielderror"
|
||||
py:content="tg.errors.get('cn')" />
|
||||
<td colspan="3">
|
||||
<table class="formtable" cellpadding="2" cellspacing="0" border="0" id="${user_fields.cns.field_id}">
|
||||
<tbody>
|
||||
<?python repetition = 0
|
||||
cn_index = 0
|
||||
cn_error = tg.errors.get('cn')
|
||||
?>
|
||||
<tr py:for="cn in value_for(user_fields.cn)"
|
||||
id="${user_fields.cns.field_id}_${repetition}"
|
||||
class="${user_fields.cns.field_class}">
|
||||
|
||||
<td py:for="field in user_fields.cns.fields">
|
||||
<span><input class="textfield" type="text" id="${user_fields.cns.field_id}_${repetition}_cn" name="cns-${repetition}.cn" value="${cn}"/></span>
|
||||
<span py:if="cn_error and cn_error[cn_index]" class="fielderror"
|
||||
py:content="tg.errors.get('cn')" />
|
||||
</td>
|
||||
<?python cn_index = cn_index + 1 ?>
|
||||
<td>
|
||||
<a
|
||||
href="javascript:ExpandingForm.removeItem('${user_fields.cns.field_id}_${repetition}')">Remove</a>
|
||||
</td>
|
||||
<?python repetition = repetition + 1?>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a id="${user_fields.cns.field_id}_doclink" href="javascript:ExpandingForm.addItem('${user_fields.cns.field_id}');">Add Common Name</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -364,61 +387,170 @@ from ipagui.helpers import ipahelper
|
||||
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" for="${user_fields.telephonenumber.field_id}"
|
||||
py:content="user_fields.telephonenumber.label" />:
|
||||
<label class="fieldlabel" for="${user_fields.telephonenumbers.field_id}"
|
||||
py:content="user_fields.telephonenumbers.label" />:
|
||||
</th>
|
||||
<td>
|
||||
<span py:replace="user_fields.telephonenumber.display(value_for(user_fields.telephonenumber))" />
|
||||
<span py:if="tg.errors.get('telephonenumber')" class="fielderror"
|
||||
py:content="tg.errors.get('telephonenumber')" />
|
||||
<td colspan="3">
|
||||
<table class="formtable" cellpadding="2" cellspacing="0" border="0" id="${user_fields.telephonenumbers.field_id}">
|
||||
<tbody>
|
||||
<?python repetition = 0
|
||||
tele_index = 0
|
||||
tele_error = tg.errors.get('telephonenumber')
|
||||
?>
|
||||
<tr py:for="tele in value_for(user_fields.telephonenumber)"
|
||||
id="${user_fields.telephonenumbers.field_id}_${repetition}"
|
||||
class="${user_fields.telephonenumbers.field_class}">
|
||||
|
||||
<td py:for="field in user_fields.telephonenumbers.fields">
|
||||
<span><input class="textfield" type="text" id="${user_fields.telephonenumbers.field_id}_${repetition}_telephonenumber" name="telephonenumbers-${repetition}.telephonenumber" value="${tele}"/></span>
|
||||
<span py:if="tele_error and tele_error[tele_index]" class="fielderror"
|
||||
py:content="tg.errors.get('telephonenumber')" />
|
||||
</td>
|
||||
<?python tele_index = tele_index + 1 ?>
|
||||
<td>
|
||||
<a
|
||||
href="javascript:ExpandingForm.removeItem('${user_fields.telephonenumbers.field_id}_${repetition}')">Remove</a>
|
||||
</td>
|
||||
<?python repetition = repetition + 1?>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a id="${user_fields.telephonenumbers.field_id}_doclink" href="javascript:ExpandingForm.addItem('${user_fields.telephonenumbers.field_id}');">Add Work Number</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" for="${user_fields.facsimiletelephonenumbers.field_id}"
|
||||
py:content="user_fields.facsimiletelephonenumbers.label" />:
|
||||
</th>
|
||||
<td colspan="3">
|
||||
<table class="formtable" cellpadding="2" cellspacing="0" border="0" id="${user_fields.facsimiletelephonenumbers.field_id}">
|
||||
<tbody>
|
||||
<?python repetition = 0
|
||||
fax_index = 0
|
||||
fax_error = tg.errors.get('facsimiletelephonenumber')
|
||||
?>
|
||||
<tr py:for="fax in value_for(user_fields.facsimiletelephonenumber)"
|
||||
id="${user_fields.facsimiletelephonenumbers.field_id}_${repetition}"
|
||||
class="${user_fields.facsimiletelephonenumbers.field_class}">
|
||||
|
||||
<td py:for="field in user_fields.facsimiletelephonenumbers.fields">
|
||||
<span><input class="textfield" type="text" id="${user_fields.facsimiletelephonenumbers.field_id}_${repetition}_facsimiletelephonenumber" name="facsimiletelephonenumbers-${repetition}.facsimiletelephonenumber" value="${fax}"/></span>
|
||||
<span py:if="fax_error and fax_error[fax_index]" class="fielderror"
|
||||
py:content="tg.errors.get('facsimiletelephonenumber')" />
|
||||
</td>
|
||||
<?python fax_index = fax_index + 1 ?>
|
||||
<td>
|
||||
<a
|
||||
href="javascript:ExpandingForm.removeItem('${user_fields.facsimiletelephonenumbers.field_id}_${repetition}')">Remove</a>
|
||||
</td>
|
||||
<?python repetition = repetition + 1?>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a id="${user_fields.facsimiletelephonenumbers.field_id}_doclink" href="javascript:ExpandingForm.addItem('${user_fields.facsimiletelephonenumbers.field_id}');">Add Fax Number</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" for="${user_fields.facsimiletelephonenumber.field_id}"
|
||||
py:content="user_fields.facsimiletelephonenumber.label" />:
|
||||
<label class="fieldlabel" for="${user_fields.mobiles.field_id}"
|
||||
py:content="user_fields.mobiles.label" />:
|
||||
</th>
|
||||
<td>
|
||||
<span py:replace="user_fields.facsimiletelephonenumber.display(value_for(user_fields.facsimiletelephonenumber))" />
|
||||
<span py:if="tg.errors.get('facsimiletelephonenumber')" class="fielderror"
|
||||
py:content="tg.errors.get('facsimiletelephonenumber')" />
|
||||
<td colspan="3">
|
||||
<table class="formtable" cellpadding="2" cellspacing="0" border="0" id="${user_fields.mobiles.field_id}">
|
||||
<tbody>
|
||||
<?python repetition = 0
|
||||
mobile_index = 0
|
||||
mobile_error = tg.errors.get('mobile')
|
||||
?>
|
||||
<tr py:for="mobile in value_for(user_fields.mobile)"
|
||||
id="${user_fields.mobiles.field_id}_${repetition}"
|
||||
class="${user_fields.mobiles.field_class}">
|
||||
|
||||
<td py:for="field in user_fields.mobiles.fields">
|
||||
<span><input class="textfield" type="text" id="${user_fields.mobiles.field_id}_${repetition}_mobile" name="mobiles-${repetition}.mobile" value="${mobile}"/></span>
|
||||
<span py:if="mobile_error and mobile_error[mobile_index]" class="fielderror"
|
||||
py:content="tg.errors.get('mobile')" />
|
||||
</td>
|
||||
<?python mobile_index = mobile_index + 1 ?>
|
||||
<td>
|
||||
<a
|
||||
href="javascript:ExpandingForm.removeItem('${user_fields.mobiles.field_id}_${repetition}')">Remove</a>
|
||||
</td>
|
||||
<?python repetition = repetition + 1?>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a id="${user_fields.mobiles.field_id}_doclink" href="javascript:ExpandingForm.addItem('${user_fields.mobiles.field_id}');">Add Cell Number</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" for="${user_fields.mobile.field_id}"
|
||||
py:content="user_fields.mobile.label" />:
|
||||
<label class="fieldlabel" for="${user_fields.pagers.field_id}"
|
||||
py:content="user_fields.pagers.label" />:
|
||||
</th>
|
||||
<td>
|
||||
<span py:replace="user_fields.mobile.display(value_for(user_fields.mobile))" />
|
||||
<span py:if="tg.errors.get('mobile')" class="fielderror"
|
||||
py:content="tg.errors.get('mobile')" />
|
||||
<td colspan="3">
|
||||
<table class="formtable" cellpadding="2" cellspacing="0" border="0" id="${user_fields.pagers.field_id}">
|
||||
<tbody>
|
||||
<?python repetition = 0
|
||||
pager_index = 0
|
||||
pager_error = tg.errors.get('pager')
|
||||
?>
|
||||
<tr py:for="pager in value_for(user_fields.pager)"
|
||||
id="${user_fields.pagers.field_id}_${repetition}"
|
||||
class="${user_fields.pagers.field_class}">
|
||||
|
||||
<td py:for="field in user_fields.pagers.fields">
|
||||
<span><input class="textfield" type="text" id="${user_fields.pagers.field_id}_${repetition}_pager" name="pagers-${repetition}.pager" value="${pager}"/></span>
|
||||
<span py:if="pager_error and pager_error[pager_index]" class="fielderror"
|
||||
py:content="tg.errors.get('pager')" />
|
||||
</td>
|
||||
<?python pager_index = pager_index + 1 ?>
|
||||
<td>
|
||||
<a
|
||||
href="javascript:ExpandingForm.removeItem('${user_fields.pagers.field_id}_${repetition}')">Remove</a>
|
||||
</td>
|
||||
<?python repetition = repetition + 1?>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a id="${user_fields.pagers.field_id}_doclink" href="javascript:ExpandingForm.addItem('${user_fields.pagers.field_id}');">Add Pager Number</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" for="${user_fields.pager.field_id}"
|
||||
py:content="user_fields.pager.label" />:
|
||||
<label class="fieldlabel" for="${user_fields.homephones.field_id}"
|
||||
py:content="user_fields.homephones.label" />:
|
||||
</th>
|
||||
<td>
|
||||
<span py:replace="user_fields.pager.display(value_for(user_fields.pager))" />
|
||||
<span py:if="tg.errors.get('pager')" class="fielderror"
|
||||
py:content="tg.errors.get('pager')" />
|
||||
</td>
|
||||
</tr>
|
||||
<td colspan="3">
|
||||
<table class="formtable" cellpadding="2" cellspacing="0" border="0" id="${user_fields.homephones.field_id}">
|
||||
<tbody>
|
||||
<?python repetition = 0
|
||||
homephone_index = 0
|
||||
homephone_error = tg.errors.get('homephone')
|
||||
?>
|
||||
<tr py:for="homephone in value_for(user_fields.homephone)"
|
||||
id="${user_fields.homephones.field_id}_${repetition}"
|
||||
class="${user_fields.homephones.field_class}">
|
||||
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" for="${user_fields.homephone.field_id}"
|
||||
py:content="user_fields.homephone.label" />:
|
||||
</th>
|
||||
<td>
|
||||
<span py:replace="user_fields.homephone.display(value_for(user_fields.homephone))" />
|
||||
<span py:if="tg.errors.get('homephone')" class="fielderror"
|
||||
py:content="tg.errors.get('homephone')" />
|
||||
<td py:for="field in user_fields.homephones.fields">
|
||||
<span><input class="textfield" type="text" id="${user_fields.homephones.field_id}_${repetition}_homephone" name="homephones-${repetition}.homephone" value="${homephone}"/></span>
|
||||
<span py:if="homephone_error and homephone_error[homephone_index]" class="fielderror"
|
||||
py:content="tg.errors.get('homephone')" />
|
||||
</td>
|
||||
<?python homephone_index = homephone_index + 1 ?>
|
||||
<td>
|
||||
<a
|
||||
href="javascript:ExpandingForm.removeItem('${user_fields.homephones.field_id}_${repetition}')">Remove</a>
|
||||
</td>
|
||||
<?python repetition = repetition + 1?>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a id="${user_fields.homephones.field_id}_doclink" href="javascript:ExpandingForm.addItem('${user_fields.homephones.field_id}');">Add Home Phone</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -655,7 +787,7 @@ from ipagui.helpers import ipahelper
|
||||
group_dn = group.get('dn')
|
||||
group_dn_esc = ipahelper.javascript_string_escape(group_dn)
|
||||
|
||||
group_name = group.get('cn')
|
||||
group_name = group.get('cn')[0]
|
||||
group_descr = "[group]"
|
||||
group_type = "group"
|
||||
|
||||
@@ -685,6 +817,7 @@ from ipagui.helpers import ipahelper
|
||||
div_counter = div_counter + 1
|
||||
?>
|
||||
</div>
|
||||
<!-- a space here to prevent an empty div -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,8 @@ from ipagui.helpers import ipahelper
|
||||
src="${tg.url('/static/javascript/dynamicedit.js')}"></script>
|
||||
<script type="text/javascript" charset="utf-8"
|
||||
src="${tg.url('/static/javascript/dynamicselect.js')}"></script>
|
||||
<script type="text/javascript" charset="utf-8"
|
||||
src="${tg.url('/tg_widgets/tg_expanding_form_widget/javascript/expanding_form.js')}"></script>
|
||||
|
||||
<?python
|
||||
searchurl = tg.url('/user/edit_search')
|
||||
|
||||
@@ -57,7 +57,21 @@ else:
|
||||
<th>
|
||||
<label class="fieldlabel" py:content="fields.cn.label" />:
|
||||
</th>
|
||||
<td>${user.get("cn")}</td>
|
||||
<td>
|
||||
<table cellpadding="2" cellspacing="0" border="0">
|
||||
<tbody>
|
||||
<?python
|
||||
index = 0
|
||||
values = user.get("cn")
|
||||
if isinstance(values, str):
|
||||
values = [values]
|
||||
?>
|
||||
<tr py:for="index in range(len(values))">
|
||||
<td>${values[index]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
@@ -132,31 +146,101 @@ else:
|
||||
<th>
|
||||
<label class="fieldlabel" py:content="fields.telephonenumber.label" />:
|
||||
</th>
|
||||
<td>${user.get("telephonenumber")}</td>
|
||||
<td>
|
||||
<table cellpadding="2" cellspacing="0" border="0">
|
||||
<tbody>
|
||||
<?python
|
||||
index = 0
|
||||
values = user.get("telephonenumber", '')
|
||||
if isinstance(values, str):
|
||||
values = [values]
|
||||
?>
|
||||
<tr py:for="index in range(len(values))">
|
||||
<td>${values[index]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" py:content="fields.facsimiletelephonenumber.label" />:
|
||||
</th>
|
||||
<td>${user.get("facsimiletelephonenumber")}</td>
|
||||
<td>
|
||||
<table cellpadding="2" cellspacing="0" border="0">
|
||||
<tbody>
|
||||
<?python
|
||||
index = 0
|
||||
values = user.get("facsimiletelephonenumber", '')
|
||||
if isinstance(values, str):
|
||||
values = [values]
|
||||
?>
|
||||
<tr py:for="index in range(len(values))">
|
||||
<td>${values[index]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" py:content="fields.mobile.label" />:
|
||||
</th>
|
||||
<td>${user.get("mobile")}</td>
|
||||
<td>
|
||||
<table cellpadding="2" cellspacing="0" border="0">
|
||||
<tbody>
|
||||
<?python
|
||||
index = 0
|
||||
values = user.get("mobile", '')
|
||||
if isinstance(values, str):
|
||||
values = [values]
|
||||
?>
|
||||
<tr py:for="index in range(len(values))">
|
||||
<td>${values[index]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" py:content="fields.pager.label" />:
|
||||
</th>
|
||||
<td>${user.get("pager")}</td>
|
||||
<td>
|
||||
<table cellpadding="2" cellspacing="0" border="0">
|
||||
<tbody>
|
||||
<?python
|
||||
index = 0
|
||||
values = user.get("pager", '')
|
||||
if isinstance(values, str):
|
||||
values = [values]
|
||||
?>
|
||||
<tr py:for="index in range(len(values))">
|
||||
<td>${values[index]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" py:content="fields.homephone.label" />:
|
||||
</th>
|
||||
<td>${user.get("homephone")}</td>
|
||||
<td>
|
||||
<table cellpadding="2" cellspacing="0" border="0">
|
||||
<tbody>
|
||||
<?python
|
||||
index = 0
|
||||
values = user.get("homephone", '')
|
||||
if isinstance(values, str):
|
||||
values = [values]
|
||||
?>
|
||||
<tr py:for="index in range(len(values))">
|
||||
<td>${values[index]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user