Managing SharePoint Site User Memberships in Multiple Groups Using SPServices
I got an email yesterday from Geoff Oliver. He said he had done something pretty useful with SPServices and wondered if I would be interested in seeing it.
I manage about 115 groups in a single site collection…and that number is expected to grow. I found that when a new user came on board or changed duty positions, getting them into the right SP groups was slow and cumbersome through the SP interface.
Using the SPServices libraries, I was able to create a simple interface to add/remove a single user to/from multiple groups at once. When you identify/select a user, it will show you two select lists. The right side will list all of the groups the current user is a member of while the left box shows all the groups the user is NOT a member of. Between the two select lists are buttons to ‘move’ the groups from one side to the other for the identified user (while modifying their memberships appropriately in the process). The select boxes are configured to allow multiple select so you can usually perform the maintenance in just a couple clicks. The code fits neatly into a CEWP.
Of course I wanted to see it. Geoff’s code sounded exactly like the kind of thing that I think SPServices is good for: a useful solution that solves a business problem better than SharePoint out of the box without needing to deploy anything to the server.
Geoff describes himself as:
…a Civilian Project Manager for the Air Force, developing a SharePoint site to assist a large organization with information management and integration with Microsoft Office (sharing/synchronizing data between SharePoint sites and Word/Excel/Access files). I’ve been doing MS Office automation (using VBA) for about 15 years now and SharePoint site development for 7.
The code uses a number of Users and Groups Web Service operations that are wrapped in SPServices:
- GetUserCollectionFromSite
- GetGroupCollectionFromUser
- GetGroupCollectionFromSite
- AddUserToGroup
- RemoveUserFromGroup
Here’s Geoff’s code. It’s amazingly compact and gets the job done well.
$(document).ready(function() { //Populate the users pick list var strHTMLSiteUsers = ""; $().SPServices({ operation: "GetUserCollectionFromSite", async: false, completefunc: function(xData, Status) { $(xData.responseXML).find("User").each(function() { strHTMLSiteUsers += "<option value='" + $(this).attr("LoginName") + "'>" + $(this).attr("Name") + "</option>"; }); $("#my_SiteUsers").append(strHTMLSiteUsers); } }); RefreshGroupLists(); }); function RefreshGroupLists(){ var strHTMLAvailable = ""; var strHTMLAssigned = ""; var arrOptionsAssigned = new Array(); var intOpts = 0; var booMatch; var booErr = "false"; $("#my_SPGroupsAssigned").html(""); $("#my_SPGroupsAvailable").html(""); if($("#my_SiteUsers").attr("value") == 0){ alert("You must select a user"); return; } //Populate the Groups Assigned $().SPServices({ operation: "GetGroupCollectionFromUser", userLoginName: $("#my_SiteUsers").attr("value"), async: false, completefunc: function(xData, Status) { $(xData.responseXML).find("errorstring").each(function() { alert("User not found"); booErr = "true"; return; }); $(xData.responseXML).find("Group").each(function() { strHTMLAvailable += "<option value='" + $(this).attr("Name") + "'>" + $(this).attr("Name") + "</option>"; arrOptionsAssigned[intOpts] = $(this).attr("Name"); intOpts = intOpts + 1; }); $("#my_SPGroupsAssigned").append(strHTMLAvailable); } }); //Populate available site groups if(booErr == "false"){ $().SPServices({ operation: "GetGroupCollectionFromSite", async: false, completefunc: function(xData, Status) { $(xData.responseXML).find("Group").each(function() { booMatch = "false"; for (var i=0;i<=arrOptionsAssigned.length;i++){ if($(this).attr("Name") == arrOptionsAssigned[i]){ booMatch = "true"; break; } } if(booMatch == "false"){ strHTMLAssigned += "<option value='" + $(this).attr("Name") + "'>" + $(this).attr("Name") + "</option>"; } }); $("#my_SPGroupsAvailable").append(strHTMLAssigned); } }); } } function AddGroupsToUser(){ var i; if($("#my_SiteUsers").attr("value") == 0){ alert("You must select a user"); return; } if($("#my_SPGroupsAvailable").val() == null){ alert("You haven't selected any groups to add"); return; } else{ var arrGroups = $("#my_SPGroupsAvailable").val(); for (i=0;i<arrGroups.length;i++){ $().SPServices({ operation: "AddUserToGroup", groupName: arrGroups[i], userLoginName: $("#my_SiteUsers").attr("value"), async: false, completefunc: null }); } RefreshGroupLists(); } } function RemoveGroupsFromUser(){ var i if($("#my_SiteUsers").attr("value") == 0){ alert("You must select a user"); return; } if($("#my_SPGroupsAssigned").val() == null){ alert("You haven't selected any groups to remove"); return; } else{ var arrGroups = $("#my_SPGroupsAssigned").val(); for (i=0;i<arrGroups.length;i++){ $().SPServices({ operation: "RemoveUserFromGroup", groupName: arrGroups[i], userLoginName: $("#my_SiteUsers").attr("value"), async: false, completefunc: null }); } RefreshGroupLists(); } }
and the associated markup:
<table align="center"> <tr> <td colspan="3" style="text-align:center"> <font style="font-weight:bold">Select a User: </font> <select id="my_SiteUsers" style="width:350px;" onchange="RefreshGroupLists()"></select> </td> </tr> <tr> <th class='ms-vh2'>Available Groups</th> <th></th> <th class='ms-vh2'>Assigned Groups</th> </tr> <tr> <td class='ms-vb2'> <select id="my_SPGroupsAvailable" style="width:250px;height:450px;" multiple="multiple"></select> </td> <td> <button id="my_AddGroupsToUser" style="width:80px;" onclick="AddGroupsToUser()">>></button><br><br> <button id="my_RemoveGroupsFromUser" style="width:80px;" onclick="RemoveGroupsFromUser()"><<</button></td> <td class='ms-vb2'> <select id="my_SPGroupsAssigned" style="width:250px;height:450px;" multiple="multiple"></select> </td> </tr> </table>
And here’s the net result. It’s a simple little form that does exactly what Geoff said it would. All I had to do to get it running in my environment was to change the references to the script files to point where I have them stored. Otherwise, it worked with no modification whatsoever.
This is the sort of thing that may belong in SPServices. Of course, you can simply copy and use the code above, but perhaps some more options and additional functionality would make it even more useful. What do you think? Is this something you’d use?
And thanks for sharing, Geoff.
Couple of little things for us Copy/Pasta people.
1. Missing refrerence to start the original block of code.
2. For the table, the styles are not referenced properly to work on at least my setup. Down in the table code, the select tags should probably be corrected to match the following: style=”width:250px;height:450px”;
All I did was fix the colon on height, and added the units (px)
Worked like a charm after that, thanks muchly to both of you.
Thanks, CStone.
While it worked fine in my dev environment (WSS 3.0) even with those nits, I just made some edits to make the HTML more valid.
M.
Hello Sir, really its very helpful your articles. As i am newbie in sharepoint. I has done only 2-3 projects(not full project) in sharepoint 2010 and moss 2007.
So happy to getting knowledge from you. Thanks Sir.
What would be the missing reference to start the original block of code? I think that I have that correct but want to make sure.
John:
You need to have references to jQuery and SPServices.
M.
Thanks for the reply Mark – all I needed to do was read one of your earlier posts :) Thanks for this post.
You’re welcome!
Thank Mark (and Geoff)! This tip is much appreciated!
Thanks. You just made my life a lot easier!
Great!
M.
Good stuff Marc. Thanks for putting it out there. We will be sure to pass this along to our customers should they need it.
Sure thing, Chris!
M.
Hi Marc,
This is awesome.After the spservices era, looks like we can do anything and eveything with webservices. Thanks a lot. really really appreciate it!.
I implemented this solution in an environment where there are ~70k active users so the dropdown just crashed the page. To make this more effective in the large environment, I changed the select to be a text input and just specified that the domain\username needed to be provided and commented out the GetUsersFromCollection query. So now I provide a username, it gives me their groups, and I add/remove as intended…beautiful.
Brendan,
How could I change it to do a name lookup with search option?
I.E. Doe, John (would change to fully distinguished name on server)
Doe, John CIV etc…….
by using the same option already available in SP when searching for a user/group.
It has been a long time since I looked at this but I think that you would want to watch a regular people picker to see what methods are used to search for users by name and display the results…You could then pass the username back to this function
I have been able to get the code to start and even verified that jquery and spservices were working correctly based on another article that i found on marc’s blog. however when the page loads it generates a message stating that “you must select a user” however the user drop down is completely empty… Should i be looking somewhere in the code to ensure it is pulling the users from my site collection?
Any thoughts on another direction for me to head?
Jacob:
What versions of jQuery and SPServices are you using?
M.
Marc:
1.7.1/jquery.min.js
jquery.SPServices-0.7.0.min.js
Thanks!!
Jacob:
OK, that’s the problem, assuming you’re using the code as written. The
.find("[nodeName='User']")
syntax is no longer valid in jQuery 1.7+. In this case, you can simply do.find("User")
. The only time we had to use the nodeName appraoch was where the namespace contained a colon, as with the z:row namespace from GetListItems. To make SPServices compatible with the jQuery change, I created the SPFilterNode function.I’ve updated the code so that others won’t have issues going forward.
M.
Worked like a charm…thank you so very much for explaining the changes that you found…very helpful in understanding what happened there.
Take care!
So now i’m curious, this solution covers groups…but if i wanted to identify sites or content that the user was applied to would i be able to leverage this code in another way or would i have to go with a third party solution to dig down into those layers.
Jacob:
Take a look through the Web Services documentation to see what’s available. If you look on the SPServices site, I’ve got links to most of the options.
M.
Perfect! I was hoping it was possible.
Thanks!
Has anyone seen a third party product that provides an already hardened solution? Or do I need to write this from scratch? I would like to allow my power users to manage the SharePoint groups that they have control of. The UI that Microsoft gives for managing group membership is just not good enough when you have many members.