Author Archives: Eric Webb

SharePoint Webpart Styling

I always wondered how to style a single webpart on the page.

http://www.heathersolomon.com/blog/articles/Controlling-Single-Web-Parts-with-CSS.aspx

Been dealing with CSS for a long time and never knew about attribute selectors.  Only issue is you can’t be in Quirks mode, which does mess up the rendering of some elements in SharePoint.

CSS Framework

Just came across this today: http://www.blueprintcss.org

Looks like a good starting point for anyone developing a new website.  I know from past experience that fighting with page layouts consumed a good chunk of time when I developed a website.  So, anything that could save me time would be very useful.

Looks like it even works for Sharepoint!

Outlook 2007 Free/Busy Issues

Came across an interesting issue that I thought I would pass along.  For the longest time I couldn’t see any Free/Busy information when I used the Outlook client to connect to Exchange.  When I used webmail, the information was there.

I finally figured out why: http://www.networkworld.com/community/node/17227.

After I added the key, the info is available.  Unbelievable.

Sharepoint 2003 to 2007 Upgrade thoughts

I just completed a medium sized upgrade (83GB) for a client from Sharepoint 2003 to 2007 and thought I’d share a few things I took away from it:

  • You’re probably going to need to create a new Site Definition to map to the old one if the old one is customized at all
  • Set expectations with users upfront that while most of the things on the site will work correctly, there will be some issues (hopefully minor) that will have to be resolved post-upgrade.  For some reason, on the site I upgraded parts of the site were upgraded flawlessly while other parts had issues.
  • Perform the migration at least once in a dev environment and let users perform UAT.  Make sure you document their issues and how you fixed them.  This document proved invaluable once we did the production migration.
  • The database upgrade option is by far the safest and is recommended.

Some helpful links:

http://blogs.msdn.com/michael_yeager/archive/2007/08.aspx <– This was, by far, the most useful

http://blogs.msdn.com/sharepoint/archive/2007/03/06/how-to-upgrade-an-area-based-on-a-custom-site-definition.aspx

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

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);
        }
    }
}

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();
                }
            }
        });

}

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);
    }
}

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:


<response> 
    <name>Test</name> 
    <states> 
        <state>OH</state> 
        <state>MI</state> 
    </states> 
</response> 

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 states { get; set; }

        public User(string UserName,List 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.

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_AuthorityAuthenticated
    //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;
}

Switch to our mobile site