summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin McCarthy <kmccarth@redhat.com>2007-09-14 15:20:09 -0700
committerKevin McCarthy <kmccarth@redhat.com>2007-09-14 15:20:09 -0700
commit65e4d27e99cb939926f4e9e972a9c27d81c3b976 (patch)
treea93c80510ce704aade5f501d0d1feafc3089e31f
parenta809d44429e5ce8f0b791f6f56a976f6c92b7c62 (diff)
downloadfreeipa.git-65e4d27e99cb939926f4e9e972a9c27d81c3b976.tar.gz
freeipa.git-65e4d27e99cb939926f4e9e972a9c27d81c3b976.tar.xz
freeipa.git-65e4d27e99cb939926f4e9e972a9c27d81c3b976.zip
patch queue: groupmember.patch
-rw-r--r--ipa-server/ipa-gui/ipagui/controllers.py178
-rw-r--r--ipa-server/ipa-gui/ipagui/forms/group.py5
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/groupedit.kid2
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/groupeditform.kid150
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/groupshow.kid12
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/userlistajax.kid14
6 files changed, 331 insertions, 30 deletions
diff --git a/ipa-server/ipa-gui/ipagui/controllers.py b/ipa-server/ipa-gui/ipagui/controllers.py
index 5324578f..1cc99862 100644
--- a/ipa-server/ipa-gui/ipagui/controllers.py
+++ b/ipa-server/ipa-gui/ipagui/controllers.py
@@ -120,17 +120,21 @@ class Root(controllers.RootController):
if tg_errors:
turbogears.flash("There was a problem with the form!")
- client.set_principal(identity.current.user_name)
- user = client.get_user_by_uid(uid, user_fields)
- user_dict = user.toDict()
- # Edit shouldn't fill in the password field.
- if user_dict.has_key('userpassword'):
- del(user_dict['userpassword'])
-
- # store a copy of the original user for the update later
- user_data = b64encode(dumps(user_dict))
- user_dict['user_orig'] = user_data
- return dict(form=user_edit_form, user=user_dict)
+ try:
+ client.set_principal(identity.current.user_name)
+ user = client.get_user_by_uid(uid, user_fields)
+ user_dict = user.toDict()
+ # Edit shouldn't fill in the password field.
+ if user_dict.has_key('userpassword'):
+ del(user_dict['userpassword'])
+
+ # store a copy of the original user for the update later
+ user_data = b64encode(dumps(user_dict))
+ user_dict['user_orig'] = user_data
+ return dict(form=user_edit_form, user=user_dict)
+ except ipaerror.IPAError, e:
+ turbogears.flash("User edit failed: " + str(e))
+ raise turbogears.redirect('/usershow', uid=kw.get('uid'))
@expose()
@identity.require(identity.not_anonymous())
@@ -204,6 +208,24 @@ class Root(controllers.RootController):
return dict(users=users, uid=uid, fields=forms.user.UserFields())
+ @expose("ipagui.templates.userlistajax")
+ @identity.require(identity.not_anonymous())
+ def userlist_ajax(self, **kw):
+ """Searches for users and displays list of results in a table.
+ This method is used for ajax calls."""
+ client.set_principal(identity.current.user_name)
+ users = []
+ uid = kw.get('uid')
+ if uid != None and len(uid) > 0:
+ try:
+ users = client.find_users(uid.encode('utf-8'))
+ counter = users[0]
+ users = users[1:]
+ except ipaerror.IPAError, e:
+ turbogears.flash("User list failed: " + str(e))
+
+ return dict(users=users, uid=uid, fields=forms.user.UserFields())
+
@expose("ipagui.templates.usershow")
@identity.require(identity.not_anonymous())
@@ -371,8 +393,7 @@ class Root(controllers.RootController):
rv = client.add_group(new_group)
turbogears.flash("%s added!" % kw.get('cn'))
- # raise turbogears.redirect('/groupedit', cn=kw['cn'])
- raise turbogears.redirect('/')
+ raise turbogears.redirect('/groupshow', cn=kw.get('cn'))
except ipaerror.exception_for(ipaerror.LDAP_DUPLICATE):
turbogears.flash("Group with name '%s' already exists" %
kw.get('cn'))
@@ -390,13 +411,43 @@ class Root(controllers.RootController):
turbogears.flash("There was a problem with the form!")
client.set_principal(identity.current.user_name)
- group = client.get_group_by_cn(cn, group_fields)
- group_dict = group.toDict()
+ try:
+ group = client.get_group_by_cn(cn, group_fields)
+
+ group_dict = group.toDict()
- # store a copy of the original group for the update later
- group_data = b64encode(dumps(group_dict))
- group_dict['group_orig'] = group_data
- return dict(form=group_edit_form, group=group_dict)
+ #
+ # convert members to users, for easier manipulation on the page
+ #
+ member_dns = []
+ if group_dict.has_key('uniquemember'):
+ member_dns = group_dict.get('uniquemember')
+ # remove from dict - it's not needed for update
+ # and we are storing the members in a different form
+ del group_dict['uniquemember']
+ if not(isinstance(member_dns,list) or isinstance(member_dns,tuple)):
+ member_dns = [member_dns]
+
+ # TODO: convert this into an efficient (single) function call
+ member_users = map(
+ lambda dn: client.get_user_by_dn(dn, ['givenname', 'sn', 'uid']),
+ member_dns)
+
+ # Map users into an array of dicts, which can be serialized
+ # (so we don't have to do this on each round trip)
+ member_dicts = map(lambda user: user.toDict(), member_users)
+
+ # store a copy of the original group for the update later
+ group_data = b64encode(dumps(group_dict))
+ member_data = b64encode(dumps(member_dicts))
+ group_dict['group_orig'] = group_data
+ group_dict['member_data'] = member_data
+
+ return dict(form=group_edit_form, group=group_dict, members=member_dicts)
+ except ipaerror.IPAError, e:
+ turbogears.flash("User show failed: " + str(e))
+ turbogears.flash("Group edit failed: " + str(e))
+ raise turbogears.redirect('/groupshow', uid=kw.get('cn'))
@expose()
@identity.require(identity.not_anonymous())
@@ -408,27 +459,84 @@ class Root(controllers.RootController):
turbogears.flash("Edit group cancelled")
raise turbogears.redirect('/groupshow', cn=kw.get('cn'))
+ # 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:
- return dict(form=group_edit_form, group=kw,
+ return dict(form=group_edit_form, group=kw, members=member_dicts,
tg_template='ipagui.templates.groupedit')
+ group_modified = False
+
+ #
+ # Update group itself
+ #
try:
orig_group_dict = loads(b64decode(kw.get('group_orig')))
new_group = ipa.group.Group(orig_group_dict)
- new_group.setValue('description', kw.get('description'))
+ if new_group.description != kw.get('description'):
+ group_modified = True
+ new_group.setValue('description', kw.get('description'))
if kw.get('gidnumber'):
+ group_modified = True
new_group.setValue('gidnumber', str(kw.get('gidnumber')))
- rv = client.update_group(new_group)
- turbogears.flash("%s updated!" % kw['cn'])
- raise turbogears.redirect('/groupshow', cn=kw['cn'])
+ if group_modified:
+ rv = client.update_group(new_group)
+ #
+ # TODO - if the group update succeeds, but below operations fail,
+ # we needs to make sure a subsequent submit doesn't try to update
+ # the group again. Probably by overwriting the group_orig hidden
+ # field blob.
+ #
except ipaerror.IPAError, e:
turbogears.flash("User update failed: " + str(e))
- return dict(form=group_edit_form, group=kw,
+ return dict(form=group_edit_form, group=kw, members=member_dicts,
tg_template='ipagui.templates.groupedit')
+ #
+ # Add members
+ #
+ try:
+ uidadds = kw.get('uidadd')
+ if uidadds != None:
+ if not(isinstance(uidadds,list) or isinstance(uidadds,tuple)):
+ uidadds = [uidadds]
+ failed = client.add_users_to_group(uidadds, kw.get('cn'))
+ #
+ # TODO - deal with failed adds
+ #
+ except ipaerror.IPAError, e:
+ turbogears.flash("User update failed: " + str(e))
+ return dict(form=group_edit_form, group=kw, members=member_dicts,
+ tg_template='ipagui.templates.groupedit')
+
+ #
+ # Remove members
+ #
+ try:
+ uiddels = kw.get('uiddel')
+ if uiddels != None:
+ if not(isinstance(uiddels,list) or isinstance(uiddels,tuple)):
+ uiddels = [uiddels]
+ failed = client.remove_users_from_group(uiddels, kw.get('cn'))
+ #
+ # TODO - deal with failed removals
+ #
+ except ipaerror.IPAError, e:
+ turbogears.flash("User update failed: " + str(e))
+ return dict(form=group_edit_form, group=kw, members=member_dicts,
+ tg_template='ipagui.templates.groupedit')
+
+ # TODO if not group_modified
+
+ turbogears.flash("%s updated!" % kw['cn'])
+ raise turbogears.redirect('/groupshow', cn=kw['cn'])
+
+
@expose("ipagui.templates.grouplist")
@identity.require(identity.not_anonymous())
def grouplist(self, **kw):
@@ -458,7 +566,25 @@ class Root(controllers.RootController):
client.set_principal(identity.current.user_name)
try:
group = client.get_group_by_cn(cn, group_fields)
- return dict(group=group.toDict(), fields=forms.group.GroupFields())
+ group_dict = group.toDict()
+
+ #
+ # convert members to users, for display on the page
+ #
+ member_dns = []
+ if group_dict.has_key('uniquemember'):
+ member_dns = group_dict.get('uniquemember')
+ if not(isinstance(member_dns,list) or isinstance(member_dns,tuple)):
+ member_dns = [member_dns]
+
+ # TODO: convert this into an efficient (single) function call
+ member_users = map(
+ lambda dn: client.get_user_by_dn(dn, ['givenname', 'sn', 'uid']),
+ member_dns)
+ member_dicts = map(lambda user: user.toDict(), member_users)
+
+ return dict(group=group_dict, fields=forms.group.GroupFields(),
+ members = member_dicts)
except ipaerror.IPAError, e:
turbogears.flash("Group show failed: " + str(e))
raise turbogears.redirect("/")
diff --git a/ipa-server/ipa-gui/ipagui/forms/group.py b/ipa-server/ipa-gui/ipagui/forms/group.py
index fd915b2c..2a36d36e 100644
--- a/ipa-server/ipa-gui/ipagui/forms/group.py
+++ b/ipa-server/ipa-gui/ipagui/forms/group.py
@@ -9,6 +9,7 @@ class GroupFields():
cn_hidden = widgets.HiddenField(name="cn")
group_orig = widgets.HiddenField(name="group_orig")
+ member_data = widgets.HiddenField(name="member_data")
class GroupNewValidator(validators.Schema):
cn = validators.PlainText(not_empty=True)
@@ -36,11 +37,11 @@ class GroupEditValidator(validators.Schema):
description = validators.String(not_empty=False)
class GroupEditForm(widgets.Form):
- params = ['group']
+ params = ['members', 'group']
fields = [GroupFields.gidnumber, GroupFields.description,
GroupFields.cn_hidden,
- GroupFields.group_orig]
+ GroupFields.group_orig, GroupFields.member_data]
validator = GroupEditValidator()
diff --git a/ipa-server/ipa-gui/ipagui/templates/groupedit.kid b/ipa-server/ipa-gui/ipagui/templates/groupedit.kid
index 14a81618..1017e400 100644
--- a/ipa-server/ipa-gui/ipagui/templates/groupedit.kid
+++ b/ipa-server/ipa-gui/ipagui/templates/groupedit.kid
@@ -16,6 +16,6 @@
<h2>Edit Group</h2>
</div>
- ${form.display(action="groupupdate", value=group)}
+ ${form.display(action="groupupdate", value=group, members=members)}
</body>
</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/groupeditform.kid b/ipa-server/ipa-gui/ipagui/templates/groupeditform.kid
index 34b8a008..dc7eb3dd 100644
--- a/ipa-server/ipa-gui/ipagui/templates/groupeditform.kid
+++ b/ipa-server/ipa-gui/ipagui/templates/groupeditform.kid
@@ -2,15 +2,113 @@
class="simpleroster">
<form action="${action}" name="${name}" method="${method}" class="tableform">
+
+ <?python searchurl = tg.url('/userlist_ajax') ?>
+
<script type="text/javascript">
function toggleProtectedFields(checkbox) {
- gidnumberField = document.getElementById('form_gidnumber');
+ gidnumberField = $('form_gidnumber');
if (checkbox.checked) {
gidnumberField.disabled = false;
} else {
gidnumberField.disabled = true;
}
}
+
+ /*
+ * Callback used for afterFinish in scriptaculous effect
+ */
+ function removeElement(effect) {
+ Element.remove(effect.element);
+ }
+
+ function adduser(uid, cn) {
+ newdiv = document.createElement('div');
+ newdiv.appendChild(document.createTextNode(
+ cn.escapeHTML() + " (" + uid.escapeHTML() + ") "));
+
+ undolink = document.createElement('a');
+ undolink.setAttribute('href', '');
+ undolink.setAttribute('onclick',
+ 'new Effect.Fade(Element.up(this), {afterFinish: removeElement});' +
+ 'return false;');
+ undolink.appendChild(document.createTextNode("undo"));
+ newdiv.appendChild(undolink);
+
+ uidInfo = document.createElement('input');
+ uidInfo.setAttribute('type', 'hidden');
+ uidInfo.setAttribute('name', 'uidadd');
+ uidInfo.setAttribute('value', uid);
+ newdiv.appendChild(uidInfo);
+
+ newdiv.style.display = 'none';
+ $('newmembers').appendChild(newdiv);
+
+ return newdiv
+ }
+
+ function adduserHandler(element, uid, cn) {
+ newdiv = adduser(uid, cn)
+ new Effect.Fade(Element.up(element));
+ new Effect.Appear(newdiv);
+ /* Element.up(element).remove(); */
+ }
+
+ function removeuser(uid, cn) {
+ newdiv = document.createElement('div');
+ newdiv.appendChild(document.createTextNode(
+ cn.escapeHTML() + " (" + uid.escapeHTML() + ") "));
+
+ undolink = document.createElement('a');
+ undolink.setAttribute('href', '');
+ undolink.setAttribute('onclick',
+ 'new Effect.Fade(Element.up(this), {afterFinish: removeElement});' +
+ "new Effect.Appear($('member-" + uid + "'));" +
+ 'return false;');
+ undolink.appendChild(document.createTextNode("undo"));
+ newdiv.appendChild(undolink);
+
+ uidInfo = document.createElement('input');
+ uidInfo.setAttribute('type', 'hidden');
+ uidInfo.setAttribute('name', 'uiddel');
+ uidInfo.setAttribute('value', uid);
+ newdiv.appendChild(uidInfo);
+
+ newdiv.style.display = 'none';
+ $('delmembers').appendChild(newdiv);
+
+ return newdiv
+ }
+
+ function removeuserHandler(element, uid, cn) {
+ newdiv = removeuser(uid, cn);
+ new Effect.Fade(Element.up(element));
+ new Effect.Appear(newdiv);
+ /* Element.up(element).remove(); */
+ }
+
+ function enterDoSearch(e) {
+ var keyPressed;
+ if (window.event) {
+ keyPressed = window.event.keyCode;
+ } else {
+ keyPressed = e.which;
+ }
+
+ if (keyPressed == 13) {
+ return doSearch();
+ } else {
+ return true;
+ }
+ }
+
+ function doSearch() {
+ new Ajax.Updater('searchresults',
+ '${searchurl}',
+ { asynchronous:true,
+ parameters: { uid: $('uid').value } });
+ return false;
+ }
</script>
<div py:for="field in hidden_fields"
@@ -63,6 +161,56 @@
</tr>
</table>
+ <div>
+ <div class="formsection">Group Members</div>
+
+ <div style="float:right; width:50%">
+ <div>To Remove:</div>
+ <div id="delmembers">
+ </div>
+ </div>
+
+ <div>
+ <div py:for="member in members" id="member-${member.get('uid')}">
+ <?python
+ member_uid = member.get('uid')
+ member_name = "%s %s" % (member.get('givenname', ''),
+ member.get('sn', ''))
+ ?>
+ ${member_name}
+ <a href=""
+ onclick="removeuserHandler(this, '${member_uid}', '${member_name}');
+ return false;"
+ >remove</a>
+ </div>
+ </div>
+
+ </div>
+
+ <div style="clear:both">
+ <div class="formsection">Add Persons</div>
+
+ <div style="float:right; width:50%">
+ <div>To Add:</div>
+ <div id="newmembers">
+ </div>
+ </div>
+
+ <div>
+ <div id="search">
+ <input id="uid" type="text" name="uid"
+ onkeypress="return enterDoSearch(event);" />
+ <input type="button" value="Find Users"
+ onclick="return doSearch();"
+ />
+ </div>
+ <div id="searchresults">
+ </div>
+ </div>
+ </div>
+
+
+
<table class="formtable" cellpadding="2" cellspacing="0" border="0">
<tr>
<th>
diff --git a/ipa-server/ipa-gui/ipagui/templates/groupshow.kid b/ipa-server/ipa-gui/ipagui/templates/groupshow.kid
index 73eab2de..48fd3663 100644
--- a/ipa-server/ipa-gui/ipagui/templates/groupshow.kid
+++ b/ipa-server/ipa-gui/ipagui/templates/groupshow.kid
@@ -32,6 +32,18 @@
</tr>
</table>
+ <div class="formsection">Group Members</div>
+ <div py:for="member in members">
+ <?python
+ member_name = "%s %s" % (member.get('givenname', ''),
+ member.get('sn', ''))
+ ?>
+ ${member_name} (${member.get('uid')})
+ </div>
+
+ <br/>
+ <br/>
+
<a href="${tg.url('/groupedit', cn=group.get('cn'))}">edit</a>
</body>
diff --git a/ipa-server/ipa-gui/ipagui/templates/userlistajax.kid b/ipa-server/ipa-gui/ipagui/templates/userlistajax.kid
new file mode 100644
index 00000000..ebfb879b
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/userlistajax.kid
@@ -0,0 +1,14 @@
+<div xmlns:py="http://purl.org/kid/ns#">
+ <div py:if='(users != None) and (len(users) > 0)'>
+ <div>${len(users)} results returned:</div>
+ <div py:for="user in users">
+ ${user.givenName} ${user.sn} (${user.uid})
+ <a href=""
+ onclick="adduserHandler(this, '${user.uid}', '${user.cn}'); return false;"
+ >add</a>
+ </div>
+ </div>
+ <div py:if='(users != None) and (len(users) == 0)'>
+ No results found for "${uid}"
+ </div>
+</div>