My career in technology

This is part four of my exploration of a solution I recently completed, using an event handler to programmatically create and configure a site based on form data provided by users and a site template created and managed by a SharePoint power user.

So the site is built, and it looks exactly like the template. Which means links and information are pointing to and coming from the template site instead of the new site.

Time for ToDo #2:

        private void DoAllSiteCreationSteps()
        {
            //Make sure this runs with appropriate privileges to actually run the commands
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                //ToDo #1: Create the site
                DoCreateSite();
                //make sure the site has finished baking before continuing
                for (int i = 0; i < 30; i++)
                {
                    System.Threading.Thread.Sleep(1000);
                }
                //ToDo #2:  Create the site user groups
                DoAllMembershipTasks();


(Links to the other parts in the series are at the bottom of the post!)

Because we set the first boolean parameter in SPWebCollection.Add() to “true”, we created unique permissions to the site. For team members to access the site, we need to make sure they have appropriate permissions to the site.

There is a reason to do this first. One of the later steps is to set an Audience for a web part on the welcome page. You can’t set the Audience if you haven’t defined it first.

        private void DoAllMembershipTasks()
        {
            DoCreateGroups();

            OwnerGroup = GetSiteGroup(OwnerGroupName);
            MemberGroup = GetSiteGroup(MemberGroupName);

            DoGiveGroupPermissions();
            DoAddUsersToGroups();

            DoSubscribeMembersToLists();
        }

There are several steps involving creating groups, assigning permissions, assigning members to the groups, and automatically subscribing members to some of the lists in the site. For project participants, it’s not opt-in!

The first step is to create the groups that will have rights to access the site and make changes to the content.

        private void DoCreateGroups()
        {
            MemberGroupName = siteName + " Project Site Group";
            OwnerGroupName = siteName + " Project Site Owners";
            owner = BuildWeb.AllUsers["DomainName\\PowerUserName"];
            try
            {
                BuildWeb.SiteGroups.Add(MemberGroupName, owner, null, "This group has rights to the " + siteName + " Project Site.");
                BuildWeb.Update();
            }
            catch (Exception ex)
            {
                if (ex.Message != "The specified name is already in use.\n\nPlease try again with a new name.")
                {
                    LogTheError("EventHandlerName", "Error Creating " + MemberGroupName + " in DoCreateGroups(): " + ex.Message + " Stack Trace: " + ex.StackTrace);
                    //throw new Exception(ex.Message);
                }
            }
            try
            {
                BuildWeb.SiteGroups.Add(OwnerGroupName, owner, null, "This group is the owner of the " + siteName + " Project Site.");
                BuildWeb.Update();
            }
            catch (Exception ex)
            {
                if (ex.Message != "The specified name is already in use.\n\nPlease try again with a new name.")
                {
                    LogTheError("EventHandlerName", "Error Creating " + OwnerGroupName + " in DoCreateGroups(): " + ex.Message + " Stack Trace: " + ex.StackTrace);
                }
            }
        }

First I create names for the group names, and define the power user as the owner for the groups.

The syntax for creating a group is SPWeb.SiteGroups.Add(“GroupName”, SPMember owner, SPUser default user, “Group Description”)

You can see in the catch statement that I don’t want an error to be thrown if the group already exists. I mentioned earlier about a use case in which the site would get re-used for a follow-on project. The requirement for this was to add any new members to the groups, but not worry about removing any members not listed in the new request.

Running the basically same code twice is not the *best* coding practice, and if there had been a third group that needed to be created, I would have used an array and a loop. But the overhead in doing that for two groups was more than the overhead for maintaining the duplicate code, given the time constraints for the project.

I’ll cover the LogTheError(string, string) error logging method in a later post.

Note that I’m using a few more class-scoped variables that I’ll need to add –

namespace ProposalSiteApprovedEH
{
    public class ItemUpdatedEventHandler : SPItemEventReceiver
    {
        //Set up Class-scoped variables
        string parentSiteUrl;
        string siteName;
        string siteDescription;
        string siteUrl;
        string siteTemplate;
        string LOB;
        string serverURL;
        SPUser NewUser;
        SPUser owner;
        string MemberGroupName;
        string OwnerGroupName;
        SPGroup OwnerGroup;
        SPGroup MemberGroup;
        SPItemEventProperties properties;
        SPSite BuildSite;
        SPWeb BuildWeb;
        SPSite LOBSite;
        SPWeb LOBWeb;
        SPSite ContainerSite;
        SPWeb ContainerWeb;
        SPWeb ProjectRequestWeb;
        bool NeedToUpdateSubsites;
        bool NeedToModifyExistingSite;

Next up in DoAllMembershipTasks() is GetSiteGroup(string name).

        private SPGroup GetSiteGroup(string name)
        {
            foreach (SPGroup group in BuildWeb.SiteGroups)
            {
                if (group.Name.ToLower() == name.ToLower())
                {
                    return group;
                }
            }
            return null;
        }

There was another, more elegant way I could have written the code. However, a future enhancement to the code is likely to include a requirement to return objects representing other groups from user-entered data. Having this method in place provides a single method for returning the object that can be used by future enhancements. There is another elegant way (shown below) to return the SPGroup object, but it only works if you know the precise case-sensitive name of the group, so you should only use it when you are sure that is what you have.

        private void DoGiveGroupPermissions()
        {
           //!!MUST GIVE THE GROUP PERMISSIONS!!
            DoAssignPermissions("Full Control", OwnerGroupName);
            DoAssignPermissions("Contribute", MemberGroupName);
            DoAssignPermissions("Contribute", "Project Functional Managers");
            DoAssignPermissions("View Only", "Project Site Visitors");
        }

Huh. Look at the comments in the code. Gee, was there a reason why I wrote “!!MUST GIVE THE GROUP PERMISSIONS!!” in all caps with all those exclamation points?

You bet there was!

Aside from the obvious that users won’t be able to access the site if the group they are a member of doesn’t have rights, I ran into an interesting problem. Remember at the top of the post that I mentioned setting the Audience for a web part on a web part page? Well, when building the code, I had the groups defined and members added to the groups, but hadn’t yet assigned rights to the groups, as I was awaiting clarification on the rights requirements for each group.

In testing what I had written, I discovered when I assigned an Audience to the web part, it would disappear from my view of the web part page, even though I had full control rights to the page AND I was a member of the group assigned as Audience for the web part. Even more peculiar, when I removed myself from the group assigned as Audience, the web part would reappear on the page! Why was the web part disappearing for members of the group that was defined to be the only group to see it?

Because the group I was a member of had no rights to the site! Though I still had full control of the page, the lack of rights for the audience group meant that the web part no longer appeared for members of the Audience group!

Fun, huh?

        private void DoAssignPermissions(string PermissionLevel, string GroupName)
        {
            SPRoleAssignment assignment;
            SPRoleDefinition roleApp;
            try
            {
                assignment = new SPRoleAssignment(BuildWeb.SiteGroups[GroupName]);
                roleApp = BuildWeb.RoleDefinitions[PermissionLevel];
                assignment.RoleDefinitionBindings.Add(roleApp);
                BuildWeb.RoleAssignments.Add(assignment);
                BuildWeb.Update();
            }
            catch (Exception ex)
            {
                LogTheError("ProposalSiteApprovedEH", "Error Creating " + PermissionLevel + " for " + GroupName + " in DoAssignPermissions(): " + ex.Message + " Stack Trace: " + ex.StackTrace);
            }
        }

The code takes the GroupName and returns the Group object using BuildWeb.SiteGroups[string]. As I mentioned above, only use this when you know you have the precise case-sensitive group name.

It also takes the string name of the permission level and returns the Role Definition for that permission level. It takes the role definition and binds it to the group for that web site.

Now that there are groups with permissions, it is time to add some members to the groups. Next time!

I’d love your feedback. Is there something you think I could be doing better? Are there questions about the code I haven’t answered? Am I full of it? Let me know! Feel free to use these code samples in accordance with my usage policy.

Check out Part One here (The Setup).
Check out Part Two here (The Decision).
Check out Part Three here (If You Built It…).
You are reading Part Four.
Check out Part Five here (Bring ‘Em All In).
Check out Part Six here (Ride the CAML!).
Check out Part Seven here (Pointing The Way).
Check out Part Eight here (Taking Part!).
Check out Part Nine here (Deconfigured).
Part Ten (The Log Blog) is coming!

More posts about SharePoint.

Comments on: "Automated SharePoint Site Provisioning Solution – Act Four (…They Will Come)" (9)

  1. Hi Jim, really nice article series. Have you not consider using SPLongOperation to display the progress while operations are running. Using Threading Sleep is not really the best practice, particularly in web applications (see here http://www.sharemuch.com/2010/01/22/displaying-sharepoint-2010-processing-page-during-your-long-running-custom-operations/).

    Cheers,
    C. Marius

    • SPLongOperation looks like a nice add-on to the solution, I’ll consider it for the next rev, by which point we’ll be on SP2010.

      (This soulution was built for SP2007).

      Can you enlighten me on the issues with Threading.Sleep? What would you suggest as an alternative for making the event handler take a break and wait for SharePoint to catch up before continuing on?

  2. […] Automated SharePoint Site Provisioning Solution – Act Four (…They Will Come) May 20102 comments 5 […]

  3. […] Automated SharePoint Site Provisioning Solution – Act Four (…They Will Come) […]

  4. […] Automated SharePoint Site Provisioning Solution – Act Four (…They Will Come) […]

  5. Hardik Shah said:

    BuildWeb.SiteGroups[string] –> the string is not case sensitive. This is best way to get SPGroup.

  6. […] Automated SharePoint Site Provisioning Solution – Act Four (…They Will Come) […]

Leave a comment