Sogeti principal consultant, David Giard, has an online technology show called “Technology and Friends”.   David is approaching his 60th episode and to recognize him for all of his great work I wanted to highlight a few of the videos he did with our local Sogeti consultants.    Make sure you check out his public speaking schedule where you can have a chance to see David live and possibly end up in one of his videos!

Sai Naik
Sogeti consultant Sai Naik describes the advantages of SharePoint and where it is a recommended solution.

David Truxall
Principal consultant David Truxall discusses the art of debugging and dives into WinDbg and other tools to debug production issues.

Jesse Murray
Principal consultant Jesse Murray shares his opinions on how to implement SharePoint solutions “the right way”.

Mike Hacker
Director Mike Hacker discusses issues around single sign-on.

Grok Talk Online – Debugging with WinDbg
First Sogeti Grok talk to be recorded and hosted online. 

Check out even more great videos produced and edited by David Giard.


In part 1 I described why a developer might choose to use a control adapter for branding out of the box SharePoint web parts.  In part 2 I showed how to create a control adapter and how you can deploy it to work with SharePoint.   In this final post for the series I will show an easier way for integrating control adapters into your SharePoint environment.

As you saw in part 2 the primary way to configure a control adapter is by adding a .browser file to your web applications app_browsers folder.  Ensuring that the file is created properly and deployed to all of the web front ends can be a complex and sometimes painful process.   An easier method is to add a little bit of code in your custom master page file.   Of course I am assuming here that since we are discussing branding you do have a custom master page with a code behind file.

To help attach a control adapter to a specific control type we will use a static helper method shown below.   This method should be placed in either a utility class or right within your custom master page class.   For the Destination Oakland site I placed it right within the custom master page class.

       private static void AddControlAdapterToType<T>(Type controlType) where T : ControlAdapter, new()
       {
           if (controlType == null)
           {
               throw new ArgumentNullException("controlType", "This argument can not be null!");
           }

           var adapters = HttpContext.Current.Request.Browser.Adapters;
           var key = controlType.AssemblyQualifiedName;
           if (!adapters.Contains(key))
           {
               var adapter = typeof(T).AssemblyQualifiedName;
               adapters.Add(key, adapter);
           }
       }

 

We will use this helper method in the constructor of the master page class to bind our custom adapter to specific controls.   Below is an example from the Destination Oakland web site.

public DestinationOaklandMaster()
{
    //Add our custom branding control adapter to brand the OOB web parts
    AddControlAdapterToType<BrandingAdapter>(typeof(Microsoft.SharePoint.WebPartPages.ContentEditorWebPart));
    AddControlAdapterToType<BrandingAdapter>(typeof(Microsoft.SharePoint.WebPartPages.ImageWebPart));
    AddControlAdapterToType<BrandingAdapter>(typeof(Microsoft.SharePoint.WebPartPages.ListViewWebPart));
    AddControlAdapterToType<BrandingAdapter>(typeof(Microsoft.SharePoint.WebPartPages.PageViewerWebPart));

    this.Load += new EventHandler(Page_Load);
}

The code in the constructor of the DestinationOaklandMaster class binds my custom control adapter class “BrandingAdapter” to 4 different out of the box SharePoint controls.    I find binding control adapters to controls using this method much more simpler than creating and deploying a .Browser xml file on all of the SharePoint web front ends.

As you have seen through this series of posts, branding out of the box SharePoint controls can be made easier by using custom control adapters.   Remember that anytime you introduce more code into your environment you could potentially see a performance impact.   In the case with Destination Oakland I saw no noticeable impact on the performance of the server when using a single custom control adapter bound to the 4 different out of the box SharePoint controls. 


Over the past year I have been working at Oakland County on many SharePoint projects including their Intranet, Extranet and public Parks and Recreation web site.   Recently Oakland county received an award for a SharePoint blog based application used for collecting IT cost reduction ideas.  Below is a recent announcement about this award.


Oakland County Executive L. Brooks Patterson announced recently that an internal cost reduction blog application created and deployed by Sogeti won the “Best Application Serving a Public Organization” award at the Michigan Digital Government Summit in Lansing on October 14, 2009.   Oakland County also received an award for their county web site Video Center.

“These awards underscore Oakland County’s continuing commitment to creating cost-effective programs for the benefit of our citizens,” says Patterson.

The Cost Reduction/Investment Blog lets county government employees discuss budgetary suggestions to bring down the county’s overall operating costs. The blog has helped the County’s IT department identify hundreds of thousands of dollars in savings. Government Technology magazine (http://www.govtech.com/gt/706827) has featured the Cost Reduction/Investment Blog in its publication.

“In a tough economy, technology helps us do more with less. Oakland County’s eGovernment programs are cost-efficient and we’ve seen substantial returns on our technology investments. Less money spent on government operations means more money is available for providing better service to our citizens,” says Phil Bertolini, Oakland County chief information officer and deputy county executive.

Oakland County recently ranked first among counties with populations of half a million or more in the 2009 Center for Digital Government Digital Counties Survey. Learn more about Oakland County’s online services and eGovernment programs at www.oakgov.com.


In part 1 I described why a developer might choose to use a control adapter for branding out of the box SharePoint web parts.  In part 2 I will explain how to create a control adapter and how you can deploy it to work with SharePoint.   In part 3 I will show an easier way for integrating control adapters into your SharePoint environment.

A control adapter allows a developers to change the default rendering behavior of any control.  In the case of SharePoint we can use the control adapter to modify how all or specific out of the box web parts are rendered.  This allows us to easily apply custom classes, styles and tags to the rendered output so we can control the branding of those web parts.

The branding that I wanted to accomplish was to have all 4 corners of the web parts rounded and put styles in place to complete the intended effect.   The final branding of the web parts can be seen on the public website I recently completed called Destination Oakland.   The inside pages of the site are SharePoint publishing pages with a primary content publishing zone in the left and web parts along the right side.  This is where you will see the control adapter in action.

To create a control adapter we start with a .NET class that inherits from the System.Web.UI.Adapters.ControlAdapter base class.   We then override the Render method to modify the HTML that will be generated by the controls which are associated to the control adapter.

Below is an example control adapter used on the public SharePoint web site www.destinationoakland.com.    

public class BrandingAdapter : ControlAdapter
    {
        protected override void Render(System.Web.UI.HtmlTextWriter writer)
        {
            var webpart = Control as WebPart;
            webpart.ChromeType = PartChromeType.None;

            StringWriter stringWriter = new StringWriter();
            using (HtmlTextWriter html = new HtmlTextWriter(stringWriter))
            {
                base.Render(html);
            }

            //Start new table to format rounded corners.
            HtmlTable tblBranding = new HtmlTable();
            tblBranding.CellSpacing = 0;
            tblBranding.CellPadding = 0;
            tblBranding.Style.Add("width", "100%");

            #region Header
            HtmlTableRow trHeader = new HtmlTableRow();
            HtmlTableCell tcTopLeftCorner = new HtmlTableCell();
            tcTopLeftCorner.Style.Add("width", "6px");
            Image imgTopLeftCorner = new Image();
            imgTopLeftCorner.ImageUrl = Helper.GetSubPageImageURL(HttpContext.Current, "subheaderleft.png",true);
            tcTopLeftCorner.Controls.Add(imgTopLeftCorner);
            trHeader.Cells.Add(tcTopLeftCorner);

            HtmlTableCell tcTopCenter = new HtmlTableCell();
            tcTopCenter.Style.Add("background-image", Helper.GetSubPageImageURL(HttpContext.Current, "subheaderfill.png",false));
            tcTopCenter.Style.Add("background-repeat", "repeat-x");
            tcTopCenter.Controls.Add(new LiteralControl("<div class='destoak-webpartTitleArea'>" + webpart.Title + "</div>"));
            trHeader.Cells.Add(tcTopCenter);

            HtmlTableCell tcTopRightCorner = new HtmlTableCell();
            tcTopRightCorner.Style.Add("width", "6px");
            Image imgTopRightCorner = new Image();
            imgTopRightCorner.ImageUrl = Helper.GetSubPageImageURL(HttpContext.Current, "subheaderright.png",true);
            tcTopRightCorner.Controls.Add(imgTopRightCorner);
            trHeader.Cells.Add(tcTopRightCorner);

            tblBranding.Rows.Add(trHeader);
            #endregion

            #region Content
            HtmlTableRow trContent = new HtmlTableRow();
            HtmlTableCell tcContentLeft = new HtmlTableCell();
            tcContentLeft.Style.Add("background-image", Helper.GetTransparentImageURL(HttpContext.Current,"/_layouts/images/DestinationOakland/subpages/contentLeftShadow.png"));
            tcContentLeft.Style.Add("background-repeat", "repeat:y");
            trContent.Cells.Add(tcContentLeft);

            HtmlTableCell tcContentCenter = new HtmlTableCell();
            tcContentCenter.Style.Add("background-image", "/_layouts/images/DestinationOakland/subpages/contentFill.png");
            tcContentCenter.Style.Add("background-repeat", "repeat");
            tcContentCenter.Controls.Add(new LiteralControl("<div class='destoak-webpartBodyArea'>" + stringWriter.ToString() + "</div>"));
            trContent.Cells.Add(tcContentCenter);

            HtmlTableCell tcContentRight = new HtmlTableCell();
            tcContentRight.Style.Add("background-image", Helper.GetTransparentImageURL(HttpContext.Current,"/_layouts/images/DestinationOakland/subpages/contentRightShadow.png"));
            tcContentRight.Style.Add("background-repeat", "repeat:y");
            trContent.Cells.Add(tcContentRight);

            tblBranding.Rows.Add(trContent);
            #endregion

            #region Footer
            HtmlTableRow trFooter = new HtmlTableRow();
            HtmlTableCell tcBottomLeftCorner = new HtmlTableCell();
            tcBottomLeftCorner.Style.Add("width", "6px");
            Image imgBottomLeftCorner = new Image();
            imgBottomLeftCorner.ImageUrl = Helper.GetTransparentImageURL(HttpContext.Current,"/_layouts/images/DestinationOakland/subpages/contentleftcorner.png");
            tcBottomLeftCorner.Controls.Add(imgBottomLeftCorner);
            trFooter.Cells.Add(tcBottomLeftCorner);

            HtmlTableCell tcBottomCenter = new HtmlTableCell();
            tcBottomCenter.Style.Add("background-image", Helper.GetTransparentImageURL(HttpContext.Current,"/_layouts/images/DestinationOakland/subpages/contentbottomshadow.png"));
            tcBottomCenter.Style.Add("background-repeat", "repeat:x");
            trFooter.Cells.Add(tcBottomCenter);

            HtmlTableCell tcBottomRightCorner = new HtmlTableCell();
            tcBottomRightCorner.Style.Add("width", "6px");
            Image imgBottomRightCorner = new Image();
            imgBottomRightCorner.ImageUrl = Helper.GetTransparentImageURL(HttpContext.Current,"/_layouts/images/DestinationOakland/subpages/contentrightcorner.png");
            tcBottomRightCorner.Controls.Add(imgBottomRightCorner);
            trFooter.Cells.Add(tcBottomRightCorner);

            tblBranding.Rows.Add(trFooter);
            #endregion

            writer.Write("<div class='destoak-webpartTable'>");
            tblBranding.RenderControl(writer);
            writer.Write("</div>");

        }
    }

 

It is recommended that you build and deploy the control adapter as part of your SharePoint branding WSP installer.  For testing purposes you can manually deploy the assembly to your web front ends global assembly cache.

One last thing must be accomplished before the control adapter will work.  You need to add a browser file to the App_Browsers folder for your specific IIS SharePoint web application.  (Remember to do this on ALL web front end servers)   This file tells IIS to apply a control adapter to a specific control type while rendering the web page.   For example I may indicate that all Microsoft.SharePoint.WebPartPages.ContentEditorWebPart controls are rendered using my custom branding ControlAdapter. 

By default SharePoint 2007 has a browser file called compat.browser located in the App_Browsers folder.  You could modify that file or you can create your own file that ends with the extension of .browser and place it in the App_Browsers folder.   Below is the contents of an example .browser file.  You will need to modify it to reference your specific control adapter class name and the control you wish to brand.

<browsers>

   <browser refID=”Default”>

      <controlAdapters>

            <adapter controlType=”Microsoft.SharePoint.WebPartPages.ContentEditorWebPart”

                     adapterType=”MyNamespace.MyControlAdapter” />

       </controlAdapters>

   </browser>

</browsers>

One drawback about this method is that it is not easy to create and deploy a .browser file to all of the SharePoint web front end servers via a .WSP installer.  In part 3 of this series I will show you how to hook up a control adapter via code in the web sites masterpage.   This will simplify the process of using control adapters within SharePoint.


Recently Sogeti Principal consultant David Truxall delivered a presentation on Debugging with WinDbg.   Check out the presentation here:

http://www.davidgiard.com/2009/09/26/SogetiGrokTalkOnlineDebuggingWithWinDbg.aspx


One of the requirements I see appear over and over when working with custom SharePoint deployments is the need for medium to complex branding.  In the case of an intranet deployment the branding usually is more basic and functional.  Possibly some minor master page changes, a new set of styles, and replacing some images is all that is required.   When it comes to public facing SharePoint sites the branding suddenly can become much more complex.

With public facing sites it seems that completely new master pages, page layouts, styles, images, web parts and dynamic content (jQuery, Ajax, etc…) is required.   Most of the branding efforts are pretty straight forward.  The difficult part can be branding the out of the box SharePoint web parts.   If you have ever tried to do something fairly common such as adding full 4 rounded corner effect to a web part you will find that it can be a difficult if not impossible.   This is because the out of the box web parts have very limited CSS classes defined, and without those CSS classes we will have a very tough time creating a cross browser style to match our designers intention.   This is where control adapters come in and can help.

A control adapter is a simple class that when applied can override the rendering of any web control.   Using a control adapter we can easily wrap additional HTML tags with CSS classes around the out of the box web part so we can get the full 4 rounded corner effect our designer was after. 

Creating a control adapter is simple.  Start with a single class that inherits from the ControlAdapter base class.   Override the Render method and implement your own rendering basing it on the original controls rendered content.   Once you have a control adapter compiled with a strong name it can be deployed as part of a SharePoint solution package.   With a little bit of custom code behind your master page you can easily apply the control adapter to selected out of the box SharePoint web controls. 

In an upcoming blog post I will go into detail on how to create a control adapter and how to register that for use with SharePoint.


New press release covering our recent Microsoft partner award:

http://www.prweb.com/releases/Sogeti_USA/customer_experience/prweb2808404.htm


Problem: When creating a page in SharePoint 2007 a user that is part of the members group (or another group that has proper permissions on the pages library) receives an access denied error message.

Possible Solution: Check the permissions of the master page document library to ensure that the users creating pages have at least read access to the files.  In our case we needed to make sure that the members group had read access.

Possible Cause:  In our situation we had taken a subsite and redeployed it as a site collection in a new web application that was enabled for anonymous users.   This caused the master page gallery permissions to be inaccurate.   Once we fixed the permissions in the master page gallery the users in the members group could now create pages.


Working on a recent project I had the need to create a custom calendar control web part.   One of the requirements I had was to make sure that when users navigated the calendar the whole page didn’t post back.    I knew a simple solution, using an UpdatePanel.   I figured this would take me just a few minutes to get the web.config file prepped with the necessary ajax updates and then wrap my calendar control with an UpdatePanel.

Since I am working on a post SP1 version of SharePoint I didn’t need to do any of the javascript hacks to get the UpdatePanel to work.  I should have been able to just wrap the calendar control and be on my way.   However, things did not go as planned.   Everytime I tested the web part the full page would post back instead of just updating the calendar.   What was wrong?

It turns out the answer was a simple one.  I had forgotten to set the ID property of my calendar control.  This made it impossible for the updatepanel to work properly and caused the full page to post back.    Once the ID was in place the updatepanel worked as expected.


This past weekend Myself along with 2 other consultants from the Detroit Sogeti office participated in the Ann Arbor Give Camp.   This is a 2 1/2 day event that starts Friday evening and wraps up on Sunday late afternoon.   The Give Camp connects talented developers with non-profit organizations in the local area with the goal to help the non-profit with some software or website need.

 

My team worked on a project for Resource Genesee.   Resource Genesee collaborates with local charities, non profit organizations, businesses and educational institutions to fulfill their mission: Mobilize people and resources to build a thriving, vibrant community.

 

Resource Genesee asked for help creating a new web presence that could be easily maintained by their limited staff.   We accomplished this task by implementing a site built on DotNetNuke with a new fresh look and feel.    I think an email I received from the Julia Zaher, the Director of Volunteer Services, says it all: 

 

“Really, I can’t tell you how much this means. I’m still stunned that so many professionals would give of their time and talents like that. I’ll definitely be spreading the word to our community. I am forever grateful!”

 

I had a blast at the Give Camp and I look forward to a bigger event next year!   Check out all of the great things that happened at the give camp here: 

 

Give Camp Video

Mike Luttenberger’s experience at Give Camp

The geeks give back to those that Give year round

Michigan Give Camp Follow Up

 

Also search on twitter for #aagivecamp for some great posts