New Sharepoint Book

Here’s a new Sharepoint book that my manager Sean McDonough and John Ferringer co-wrote:

SharePoint 2007 Disaster Recovery Guide

Book Description:

Microsoft’s SharePoint technology gives organizations access to a wide range of tools and functionality designed to encourage collaboration, manage documents, discover shared knowledge, and much more. As the use of these resources through SharePoint grows, so does an organization’s dependency upon the availability and reliability of its SharePoint infrastructure. Disaster recovery is not a new concept in the realm of information technology nor is it unique to SharePoint, but as with any specialized technical platform, disaster recovery planning for SharePoint presents unique challenges and approaches. The problem is that SharePoint disaster recovery is not as simple as backing up your discs or load balancing your servers. It requires specialized activities to save data for recovery and to subsequently properly recover that same data. SharePoint professionals need a cohesive and comprehensive resource that guides them through the steps of planning, implementing, and testing the right disaster recovery plan for their situation. The “SharePoint 2007 Disaster Recovery Guide” provides such a resource. It provides a unique guidebook to all the necessary best practices, procedures, and tools for disaster recovery in SharePoint, such as how to plan for disaster recovery in SharePoint, and how to setup SharePoint, SQL Server, operating system, and file system backups step-by-step through SQL Server or STSADM.exe. The negative and positive aspects of every solution are detailed so that users can pick the disaster recovery plan that’s right for them. This is an essential guide for any IT professional tasked with protecting their organization’s valuable SharePoint data.

Amazon Link

Filed under:general, sharepoint

Welcome Page Updater

During a recent uprade from Sharepoint 2003 to 2007, I noticed that some of the sites’ welcome pages weren’t getting set correctly. For some reason, they were set to UpgLandingPgRedir.aspx, which redirected to nowhere. Very convenient; must be a feature.

Here’s a piece of code I wrote that looks at every weclome page in your site collection and changes it if necessary. The Pause() isn’t necessary; I found it helpful to verify the changes that were being made.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;

namespace SPWelcomePageUpdater
{
    class SPWelcomePageUpdater
    {
        static void Main(string[] args)
        {
            Console.Write("Please enter the URL of the Sharepoint site: ");
            string url = Console.ReadLine();
            using (SPSite siteCollection = new SPSite(url))
            {
                SPWebCollection coll = siteCollection.AllWebs;

                for (int i = 0; i < coll.Count; i++)
                {
                    using (SPWeb targetWeb = coll[i])
                    {
                        SPList pagesLib = targetWeb.Lists["Pages"];
                        SPFile newWelcomePage = null;
                        foreach (SPListItem li in pagesLib.Items)
                        {
                            if (li.Name == "default.aspx")
                            {
                                newWelcomePage = li.File;
                            }
                        }

                        if (newWelcomePage != null)
                        {
                            if (PublishingWeb.IsPublishingWeb(targetWeb))
                            {
                                PublishingWeb publishingWeb = 
                                     PublishingWeb.GetPublishingWeb(targetWeb);
                                Console.WriteLine(publishingWeb.DefaultPage.Name);
                                if (publishingWeb.DefaultPage.Name=="UpgLandingPgRedir.aspx")
                                {
                                    // this sets the new welcome page
                                    publishingWeb.DefaultPage = newWelcomePage;    
                                    publishingWeb.Update();
                                    Console.WriteLine(publishingWeb.Name 
                                         + " welcome page updated.");
                                    Pause();
                                }
                                else if (!publishingWeb.DefaultPage.Url.ToUpper()
                                                .Contains("PAGES"))
                                {
                                    // this sets the new welcome page
                                    publishingWeb.DefaultPage = newWelcomePage;    
                                    publishingWeb.Update();
                                    Console.WriteLine(publishingWeb.Name 
                                         + " welcome page re-updated.");
                                    Pause();
                                }
                            }
                        }
                    }
                }
            }
        }
        static void Pause()
        {
            Console.WriteLine("Press any key to continue...");

            Console.ReadKey(true);
        }
    }
}
Filed under:sharepoint

Infopath Item Level Security

Man it’s been awhile since I posted last.  Since nobody reads this, I guess it doesn’t matter.

Item level security on Infopath forms has been bugging me awhile.  It should be easy to set permissions for a form in the form library by using Rules or some other method.  However, that’s not the case.

You can do it using EventReceivers, though.  And I’m happy to say that it works pretty well.  Here’s the code:

public override void ItemAdded(SPItemEventProperties properties)
{
        Guid siteID, webID, listID;
        int itemID;
        SPUser curr;
        listID = properties.ListId;

        itemID = properties.ListItem.ID;
        using (SPWeb web = properties.OpenWeb())
        {
            siteID = web.Site.ID;
            webID = web.ID;
            curr = web.CurrentUser;
        }
        SPSecurity.RunWithElevatedPrivileges(delegate()
        {
            using (SPSite site = new SPSite(siteID))
            {
                using (SPWeb web = site.OpenWeb(webID))
                {
                    SPList list = web.Lists[listID];

                    SPRoleAssignmentCollection roles = list.RoleAssignments;
                    SPListItem addedItem = list.GetItemById(itemID);
                    addedItem.BreakRoleInheritance(false);

                    SPRoleDefinition roleDefinition =
                    web.RoleDefinitions.GetByType(SPRoleType.Reader);

                    SPRoleAssignment roleAssignment =
                    new SPRoleAssignment(curr.LoginName.ToString(),
                    curr.Email.ToString(), curr.Name, "");

                    roleAssignment.RoleDefinitionBindings.Add(roleDefinition);

                    addedItem.RoleAssignments.Add(roleAssignment);

                    this.DisableEventFiring();

                    web.AllowUnsafeUpdates = true;
                    addedItem.Update();

                    this.EnableEventFiring();
                }
            }
        });

}
Filed under:sharepoint

Sharepoint web services + autocompleteextender

Oh boy that was fun.  I’m not even going to get into what I had to do and how long it took to get this working.  However, some code below will be helpful for those of you that want to try this yourselves (it’s not mine; I pulled it from the Smarttools source).  This solves the issue that Sharepoint has with Ajax postbacks/callbacks.

Also, you’re going to want to make sure that your “servicepath” attribute of the autocompleteextender is setup like this: “~/_vti_bin/nameofyourwebservice.asmx” or else you’ll get prompted to log in every time you call the service (which, incidentally, should be deployed to the ISAPI directory in your “12″ hive).

If you need additional details, let me know.

ScriptManager sm = ScriptManager.GetCurrent(this.Page);

if (sm == null)
{
    //create new ScriptManager and EnablePartialRendering

    sm = new ScriptManager();
    sm.EnablePartialRendering = true;
    sm.EnableScriptLocalization = true;

    // Fix problem with postbacks and form actions (DevDiv 55525)
    Page.ClientScript.RegisterStartupScript(typeof(AgencyCodePicker),
    this.ID, "_spOriginalFormAction = document.forms[0].action;", true);

    //tag:"form" att:"onsubmit" val:"return _spFormOnSubmitWrapper()"
    //blocks async postbacks after the first one
    //not calling "_spFormOnSubmitWrapper()" breaks all postbacks
    //returning true all the time, somewhat defeats the purpose of
    //the _spFormOnSubmitWrapper() which is to block repetitive postbacks,
    //but it allows MS AJAX Extensions to work properly
    //its a hack that hopefully has minimal effect

    if (this.Page.Form != null)
    {
        string formOnSubmitAtt = this.Page.Form.Attributes["onsubmit"];
        if (!string.IsNullOrEmpty(formOnSubmitAtt)
        && formOnSubmitAtt == "return _spFormOnSubmitWrapper();")
        {
            this.Page.Form.Attributes["onsubmit"] = "_spFormOnSubmitWrapper();";
        }
        //add the ScriptManager as the first control in the Page.Form
        //I don't think this actually matters, but I did it to be consistent
        //with how you are supposed to place the ScriptManager when used declaritevly
        this.Controls.AddAt(0, sm);
    }
}
Filed under:general

LINQ to XML subquery<

I didn’t find a clear example online about how to do a subquery of an element, so I thought I would share.

First, here’s the XML I’m reading:

&lt;response&gt; 
    &lt;name&gt;Test&lt;/name&gt; 
    &lt;states&gt; 
        &lt;state&gt;OH&lt;/state&gt; 
        &lt;state&gt;MI&lt;/state&gt; 
    &lt;/states&gt; 
&lt;/response&gt;

I want to use LINQ to parse through the responses and create new objects for each one of them.  The object I created to hold the info is below:

[Serializable()]
    public class User
    {
        public string userName { get; set; }a
        public List<string> states { get; set; }

        public User(string UserName,List<string> States)
        {
            userName = UserName;
            states = States;
        }
    }

I was at a loss at how to get the state elements into my “States” list. Here’s the query I ended up using:

var responses = from response in loginResult.Descendants("response")
      select new
      {
            authenticationToken = response.Element("authentication_token").Value,
            states = (from st in response.Descendants("states").Elements()
                         select st.Value).ToList(),

      };

It was obvious after I got it and I probably should have been able to figure it out quicker, but such as life when learning something new.

Filed under:general

Sharepoint

I’ve been meaning to post my thoughts about my recent foray into Sharepoint for awhile now and since I finally have some time, here goes.

Development on Sharepoint sucks if you’re not doing it directly on the server.
-I can’t even count the number of hours I’ve spent copying a .cab file to the server, running stsadm.exe -o addwppack and restarting IIS.
Documentation for development on Sharepoint sucks.
-This was the first time I’ve ever developed against a platform with such limited documentation/examples. While it was pretty interesting, I would prefer to not do something like this again.

That being said, I thought I would post some of the code that I’ve written that may be useful. This first webpart allows you to search for users in the AD tree associated with your site:

private DataTable Search()
{
    SPSite siteCollRoot = new SPSite(SPContext.Current.Site.Url);
    DataTable dt = new DataTable();

    //must log in to the SSP and give the NT_Authority\Authenticated
    //users the Manage User Profiles and Personal site permissions
    //here (http://localhostssp/admin/_layouts/ManageServicePermissions.aspx)
    //in order for the code below to work
    //for all users
    SPSecurity.RunWithElevatedPrivileges(delegate()
    {

        using (SPSite siteColl = new SPSite(siteCollRoot.ID))
        {
            ServerContext context = ServerContext.GetContext(siteColl);
            UserProfileManager upm = new UserProfileManager(context);
            UserProfile currentProfile;
            IEnumerator enumProfs = upm.GetEnumerator();
            bool continueEnum = true;

            DataColumn dc = new DataColumn("Title");
            dt.Columns.Add(dc);

            dc = new DataColumn("EMail");
            dt.Columns.Add(dc);

            dc = new DataColumn("JobTitle");
            dt.Columns.Add(dc);

            dc = new DataColumn("Department");
            dt.Columns.Add(dc);

            dc = new DataColumn("WorkPhone");
            dt.Columns.Add(dc);

            dc = new DataColumn("ID");
            dt.Columns.Add(dc);

            dc = new DataColumn("LastName");
            dt.Columns.Add(dc);

            dc = new DataColumn("FirstName");
            dt.Columns.Add(dc);

            while (continueEnum)
            {
                try
                {
                    continueEnum = enumProfs.MoveNext();
                }

                catch (Exception e)
                {
                    Console.WriteLine("EXCEPTION in enum: " + e.ToString());
                    continueEnum = enumProfs.MoveNext();
                }
                currentProfile = (UserProfile)enumProfs.Current;

                try
                {
                    DataRow dr = dt.NewRow();
                    dr["ID"] = currentProfile.ID.ToString();
                    dr["Title"] = currentProfile["PreferredName"].ToString();
                    dr["EMail"] = currentProfile["WorkEmail"].ToString();
                    dr["JobTitle"] = currentProfile["Title"].ToString();
                    dr["Department"] = currentProfile["Department"].ToString();
                    dr["WorkPhone"] = currentProfile["WorkPhone"].ToString();
                    dr["LastName"] = currentProfile["LastName"].ToString();
                    dr["FirstName"] = currentProfile["FirstName"].ToString();

                    dt.Rows.Add(dr);
                }
                catch (Exception ex)
                {

                }

            }

        }
    });

    return dt;
}
Filed under:sharepoint

Newport Aquarium

On a whim, the wife and I decided to check out the Newport Aquarium yesterday. We were already at the Levee (enjoying Dim Sum at Pacific Moon) and really didn’t have anything else going on, so we headed over. I was a little shocked at the ticket price ($18.95) but after spending an hour there, we decided that it was worth it. They have some really cool exhibits like a “live flight” bird sanctuary and the requisite shark tank. Definitely check it out if you’ve got a free afternoon.

Filed under:life