Oct15

SharePoint Content Deployment - A Couple Lessons Learned

I've been working with a client for the last few months to develop a handful of public facing web sites running under MOSS publishing. I headed over to the UK to deploy the framework for these sites and ensure that we could do content deployments from their internal staging environment to their production environment in their DMZ. I've had some success in the past with content deployment in SharePoint but its always made me nervous even following SP1 and the post-SP1 infrastructure updates that were suppose to target many problems with content deployment. After a frustrating day without success and cryptic errors we initiated a call to MS. Our client was concerned with some regulatory compliance issues if the site wasn't deployed so we figured it warranted a call to MS to get things going. Not my first call to MS probably not my last.

 

After working with MS for almost 12 hours straight with no success our call was pushed up the ladder. In the end we finally got some resolution but I think that Content Deployment really needs some more maturing. I was hoping for a better experience with all the infrastructure updates that addressed numerous issues. It would appear that those updates only took the functionality from broken to "mostly working" status lol. The administration experience is still fairly primitive and even MS support could not agree on the best practices or even proper steps  in some sceneries. I especially didn't get the warm fuzzy with MS support mentioned about every hour that there were still known issues with Content Deployment. Unfortunately I couldn't seem to dig out of him what those known issues where.

 

I thought I might pass on a couple issues that caused us some grief:

 

1) Feature Receivers - The solution for our client was meant to be a turn key solution that could be reused on many sites. Because of this all entities for the site had to be wrapped up in features and activated on demand as needed (or from a site def). Now lookup fields can not be created declaratively because they require instance IDs that don't exist at the time of definition. A common practice is to place these within feature receivers so that when a feature is activated, in our case creating new lists and site columns among other things required lookup fields get generated along side the declarative fields. This works great until content deployment. Content deployment jobs start with an empty site then they activate the appropriate features and then they push content over. Because the feature is "reactivated" prior content deployment and in our case the lookup column is created it causes a conflicting column error during content deployment. Now I completely understand that MS can't account for things that occur in feature receivers, the possibilities could be endless, but it would be nice to have a hook somewhere in a context that allowed me to have logic in the feature receiver to deal with this scenario. We were able to get around this but being a big fan of feature receivers to overcome short comings in the CAML\Declarative feature syntax it makes me give pause to quickly slap things into feature receivers in the future when I know content deployments will be used.

 

2) Using a Blank Site for the target of Content Deployments - Starting a few months back I began creating the default site collection of a target content deployment site using the command line (stsadm). This is the only reliable way to create a truly empty site as even a blank site has artifact in it which can cause conflicts. Throughout the process with MS we were told time and time again to use a blank site template. I asked them to confirm this several times and they insisted that this was correct way of "priming" the target site. In the end we ended up using the stsadm -o createsite command which doesn't require you to specify a template and the content deployment package successfully deployed. It's pretty scary that something at the core of content deployments still has conflicting practices at MS.

 

3) Consistent naming of the webtemp.xml for custom site definitions - Our biggest teaser was that we were able to create sites using our custom site definition on the target farm but content deployment errors kept insisting that the site definition didn't exist. At first we thought it was a language issues because it mentioned 1033 several times and we were on a UK server but that wasn't the problem. Somewhere along the lines on our internal staging server our webtemp.xml file got named from webtemp[customname].xml to webtemp.[customname].xml (period separator). I can only think that we changed it by accident in passing while checking content deployment problems so this really isn't a MS problem but at the same time the error message implied that the custom site def wasn't registered when in fact it was, just with a different webtemp.xml filename. So this brings me to my biggest remaining beef with content deployments...

 

Content deployments are still very hard to diagnose and resolve the source of conflicts and issues. We cranked up logging to the highest level and that just made our life even more difficult as we had to wade through 10 times as much garbage without it contributing much to the resolution of our issue. I think 2 out of the 3 issues we had can be rather common out there. Completely legitimate but common none the less but even if they aren't common it shouldn't be this hard to figure out and resolve content deployment issues. Now that the majority of stability issues are hopefully resolved with the post SP1 infrastructure patch I think its a good time for MS to start improving the usability and manageability of content deployment beyond the level of something I would expect from a CodePlex project (no offense to all the great CodePlex projects out there lol).

 

Hopefully this may help a couple folks out there in their own Content Deployment hell :) Have any horror stories you'd like to share?

Published: Oct-15-08 | 0 Comments | 0 Links to this post

Oct11

Custom SharePoint List Definitions The Quick And Easy Way.

In the last year and half I've been almost exclusively working on MOSS publishing projects. Any developer working on these projects becomes very familiar with site columns and content types. Site Columns and Content Types are central to WCM in MOSS but they also play a very important role throughout SharePoint as a whole. In its most simplest definition Site Columns play a very similar role that standard columns do within a list but allow for reusability throughout the site collection. Groups of site columns are then organized into Content Types and associated with other lists and functionality throughout SharePoint. Additionally they offer a great deal of flexibility such as the ability to modify content types (add & removing site columns) after creation and attaching additional functionality such as a workflow to.

With all of the advanced functionality they provide in many complex scenarios such as publishing its often overlooked that they can also be used for everyday needs such as defining a simple list with custom fields. The standard declarative approach for this is to create a feature that defines your columns within the schema.xml and a subsequent ListTemplate and/or ListInstance element to register this new list with SharePoint and optionally create an instance of that list. The functionality is very powerful but at the same time can get quite complex as modifying the schema.xml can often be cryptic and error prone, not really much fun.. trust me.


A simple alternative approach is to define site columns or even reuse out of the box or existing columns, define your content type, and bind that content type to an existing list such as a generic list. Its also possible to take existing content types, such as those use for tasks, and inherit from that content type to add your own site columns and bind that to a defined task list.

 

The following is one possible approach using this technique:

 

Solution Screenshot 

 

Step 1 : Define your feature
<Feature xmlns="http://schemas.microsoft.com/sharepoint/"
         Id="F441DF54-5950-4C79-BDD1-C6960140346A"
         Title="Simple List Feature"
         Description="This feature Demonstrates the use of ContentTypes in lists."
         Scope="Site"
         Hidden="False"
         Version="1.0.0.0">

  <ElementManifests>
    <ElementManifest Location="SiteColumns.xml" />
    <ElementManifest Location="ContentTypes.xml" />
    <ElementManifest Location="List.xml" />
  </ElementManifests>

</Feature>


Step 2: Define your Site Columns

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  
  <Field SourceID="http://schemas.microsoft.com/sharepoint/v3"
           ID="{37426E2D-2F00-40E2-9BA2-EF4B1038E400}"
           Name="CustomField1"
           DisplayName="CustomField1"
           Group="SimpleList"
           Type="Text"
           Required="FALSE"
           Sealed="FALSE"
           ReadOnly="FALSE"
           Hidden="FALSE" />
  
  <Field SourceID="http://schemas.microsoft.com/sharepoint/v3"
           ID="{107D1023-54AC-43E0-8AED-65D105685184}"
           Name="CustomField2"
           DisplayName="CustomField2"
           Group="SimpleList"
           Type="Text"
           Required="FALSE"
           Sealed="FALSE"
           ReadOnly="FALSE"
           Hidden="FALSE" />
</Elements>

 

Step 3: Define your Content Type (this was is based on a standard list)

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <ContentType ID="0x010026148DD4DF324cae899D48F124390550"
               Name="SimpleList"
               Description="Simple List Content TYpe"
               Group="SimpleList">
    <FieldRefs>
      <FieldRef ID="{37426E2D-2F00-40E2-9BA2-EF4B1038E400}"
                Name="CustomField1"
                DisplayName="CustomField1"
                ShowInDisplayForm="TRUE"
                ShowInFileDlg="TRUE"
                ShowInListSettings="TRUE"
                ShowInNewForm="TRUE"
                ShowInEditForm="TRUE"
                ReadOnlyClient="FALSE" />
      <FieldRef ID="{107D1023-54AC-43E0-8AED-65D105685184}"
                Name="CustomField2"
                DisplayName="CustomField2"
                ShowInDisplayForm="TRUE"
                ShowInFileDlg="TRUE"
                ShowInListSettings="TRUE"
                ShowInNewForm="TRUE"
                ShowInEditForm="TRUE"
                ReadOnlyClient="FALSE" />
      
    </FieldRefs>
  </ContentType>
</Elements>

Step 4: Create an instance of your list and bind your content type
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <ListInstance TemplateType="100"
              RootWebOnly="false"
              Id="CustomList"
              Title="CustomList"
              Url="Lists/CustomList"
              OnQuickLaunch="true"
              FeatureId="00BFEA71-DE22-43B2-A848-C05709900100">
  </ListInstance>
  <ContentTypeBinding ContentTypeId="0x010026148DD4DF324cae899D48F124390550" ListUrl="Lists/CustomList"/>
</Elements>

 

 

As you can see its pretty straight forward. Unfortunately it doesn't come without a couple gotchas. First off the default view of your list does not contain those custom columns. When you select the item you will of course see all of your columns but they aren't listed in the default view. Secondly the default content type for the list is still available to the user. In the case of a generic list the "Item" content type is still present. So when a user enters a new item they are able to select Item or your custom content type. This may not be desirable. Also if you are using the API to insert a new item into the list it will use the list's default content type instead of the one you may be expecting. Once again this may not be desirable.

 

To overcome these restrictions we're going to create a generic feature receiver that can be reused on any implementation using this technique.

 

First thing we're going to do is to make some changes to our feature to both support a feature receiver and to pass in some properties. We're going to pass in the list name and the content type name to the feature receiver code so that we do not have to hard code any values. This will be important for reuse.
<Feature xmlns="http://schemas.microsoft.com/sharepoint/"
         Id="F441DF54-5950-4C79-BDD1-C6960140346A"
         Title="Simple List Feature"
         Description="This feature Demonstrates the use of ContentTypes in lists."
         Scope="Site"
         Hidden="False"
         Version="1.0.0.0"
         ReceiverAssembly="SimpleList, Version=1.0.0.0, Culture=neutral, PublicKeyToken=[REPLACE_THIS]"
         ReceiverClass="SimpleList.SimpleListReceiver">

  <ElementManifests>
    <ElementManifest Location="SiteColumns.xml" />
    <ElementManifest Location="ContentTypes.xml" />
    <ElementManifest Location="List.xml" />
  </ElementManifests>

  <Properties>
    <Property Key="DefaultContentType" Value="SimpleList"/>
    <Property Key="ListName" Value="CustomList"/>
  </Properties>
  
</Feature>

Next we're going to build out the feature receiver. This receiver will help us overcome two of those gotchas. First we'll set the default content type for the list and second we'll add the fields from the content type to the default view of the list.

public class SimpleListReceiver : SPFeatureReceiver 
    {
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            SPSite site = properties.Feature.Parent as SPSite;
            SPWeb web = site.OpenWeb();
            
            using (web)
            {
                string listName = properties.Feature.Properties["ListName"].ToString();
                string defaultContentTypeName = properties.Feature.Properties["DefaultContentType"].ToString();

                SPList list = web.Lists[listName];

                SetDefaultContentType(list, defaultContentTypeName);

                AddColumnsToDefaultView(list, defaultContentTypeName,web);

            }

        }

        private void SetDefaultContentType(SPList list, string defaultContentTypeName)
        {
            SPContentType defaultContentType = list.ContentTypes[defaultContentTypeName];
            list.ContentTypesEnabled = true;

            SPFolder folder = list.RootFolder;
            SPContentType[] orderedContentTypes = new SPContentType[1];
            orderedContentTypes[0] = defaultContentType;

            folder.UniqueContentTypeOrder = orderedContentTypes;
            folder.Update();

        }

        private void AddColumnsToDefaultView(SPList list, string defaultContentTypeName, SPWeb web)
        {
            SPContentType contentType = web.ContentTypes[defaultContentTypeName];
            SPView defaultView = list.DefaultView;

            foreach(SPField field in contentType.Fields)
            {
                defaultView.ViewFields.Add(field);
            }

            defaultView.Update();
            
        }

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties){}

        public override void FeatureInstalled(SPFeatureReceiverProperties properties){}

        public override void FeatureUninstalling(SPFeatureReceiverProperties properties){}

    }

 

With this new feature receiver at your disposal you should be able to quickly build out custom lists, take advantage of content types and site columns, and provide a good user experience by defaulting your content type and adding your new fields to the default view of the list.

 

I'll be uploading a zip file for download here shortly that includes this project's visual studio solution and wsp package.

Published: Oct-11-08 | 0 Comments | 0 Links to this post

Oct03

Moved over to CKS EBE 2.0

I apologize ahead of time for any broken links. I've moved from my Ghetto home grown SharePoint half broken blog mod to the CKS EBE and all I have to say is I wish I didn't wait so long. Its a great blogging platform. Those guys did a great job. I built out my "brain freeze" theme in about 20 minutes. There is a very good tutorial on the process over at Liam Cleary's blog. Kudos to the CKS team that build this out. Unfortunately I couldn't move the comments over and  think there will be a few broken links here and there and probably some dead images. I'll see about cleaning them up.

Published: Oct-03-08 | 0 Comments | 5 Links to this post

Oct02

Raleigh SharePoint Developers Guild October Meeting - Michael Lotter on Forms Server Development

I've been pretty quiet as of late because of my new position with B&R. Its been a challenge to get the Raleigh office up and running, getting 2 new SharePoint developers up to speed while managing a heavy client load to boot. The good news is although I've been quiet I have also been quite busy with some community projects. The Raleigh SharePoint Developers Guild has a new web site up and running at http://www.sharepointdevelopersguild.com . Its running off of an early release of the SharePoint UserGroup Portal I was blogging about a few months back. More to come on that soon.

I'm very much looking forward to seeing Michael Lotter's presentation on MOSS Forms Server development. If your not familiar with Forms Server its a aspect of MOSS that allows InfoPath based forms to be deployed and filled out from a web browser as opposed to requiring the InfoPath client. Additionally  it can make use of other features within SharePoint such as workflow. Michael is probably one of the best Forms Server developers in the country and it should not be missed if you're interested in learning more about Forms Server. More information can be found at the SharePoint Developers Guild Site.

Published: Oct-02-08 | 3 Comments | 0 Links to this post

Jun14

Speaking New Jersey SharePoint User Group - SharePoint for ASP.NET Developers, Wednesday June 18th

Next week I'll be heading up to New Jersey to speak at the New Jersey SharePoint User Group. My topic will be SharePoint for ASP.NET Developers. Checkout the the NJ User Group Site for registration and more details. If you're interested in leveraging some of your asp.net skills in the SharePoint context or you curious on how to get started on SharePoint development I think you'll find some good stuff in this presentation. I look forward to seeing everyone there.

Published: Jun-14-08 | 0 Comments | 1120 Links to this post

Jun11

Back from Orlando

So as most people were arriving for the IT week at TechEd I was heading home. Hopefully PDC will be worth the wait lol. I did hang out for an extra evening though and it was well worth it. I haven't seen so many drunk SharePoint guys in one place. Had a great time hanging out until the wee hours with the  B&R gang (Chris Regan, Jay Medero, Bob Fox, Michael Lotter) along with some other hardcore SharePoint geeks like Joel Olesen and Mike Watson.  Great fun guys, maybe I'll stick around next time... looks like I missed some fun Tuesday night :)

 

Joel, Chris, and Mike posing with the cops.

Joel_Chris_Mike_thumb

 

Published: Jun-11-08 | 0 Comments | 1 Link to this post

Jun08

Creating a Enhanced MOSS 404 Feature - Part 1

SharePoint out of the box provides only the default 404 page when users attempt to navigate to a page or site that doesn't exist. It has become very common place for users to expect some assistance when entering an invalid URL and also to be forwarded to the proper site when hitting vanity subsite names. For example if you were to have a product called widget whose physical location is referenced at http://www.contoso.com/products/widgets but you would like users who access http://www.contoso.com/widget to be directed to the proper site.

For complete sourcecode and solution package check out the CodePlex project at http://www.codeplex.com/sharepointsmart404 . Special thanks to Chris Regan for some creative assistance on this project.

Lets go ahead and summarize the goals of this feature:

  • Provide a friendly 404 message to the user
  • Provide some possible best bets on what they may have been attempting to look for based on the URL
  • Provide a vanity redirect mechanism
  • Implement these features with as little overhead on the general page lifecycle

Before we get started on the implementation lets take a quick peak at the end results:

image

 

To implement this feature we'll want to create a new feature that provisions several elements and assemblies to support this functionality.

1) Create a new Feature that will support a simple activation to add this functionality to a site. This feature will include elements to support a custom 404 page, receivers to register the custom 404 page, and finally the support infrastructure for the vanity management:

<Feature xmlns="http://schemas.microsoft.com/sharepoint/"
         Id="625D428C-1191-425D-9855-D68335173195"
         Title="SharePoint Smart 404"
         Description="Provides smart Page Not Found handling."
         Scope="Site"
         Hidden="False"
         Version="1.0.0.0"
         ReceiverAssembly="SharePointSmart404, Version=1.0.0.0, Culture=neutral, PublicKeyToken=eaf58ff25bb059f9"
         ReceiverClass="SharePointSmart404.SharePointSmartReceiver">

  <ElementManifests>
    <ElementManifest Location="pages.xml" />
    <ElementManifest Location="vanity.xml"/>
    <ElementFile Location="Pages\Smart404.aspx"/>
  </ElementManifests>

</Feature>

 

2) Central to the custom 404 functionality is the implementation of a custom 404 html file. Unfortunately the landing page for 404 errors within SharePoint do not support asp.net pages. Instead a HTML page receives the initial 404 error and forwards to a designated site. The custom 404 html file must be registered through the sharepoint API. As you can se we are parsing the url in javascript to obtain the subsite  details. We then create a new query string that the SharePoint search webparts will then use to provide some best bets. 

<html>
<head>
    <meta HTTP-EQUIV="Content-Type" content="text/html; charset=utf-8" />
    <meta HTTP-EQUIV="Expires" content="0" /> 
    <noscript>
        <meta http-equiv="refresh" content="0; url=/_layouts/spsredirect.aspx?noscript=1" />
    </noscript> 
    <script language="javascript" src="/_layouts/1033/init.js"></script>
   1:  
   2:     <script language="javascript" src="/_layouts/1033/core.js">
   1: </script>
   2:     <script language="javascript">
   3:         var requestedUrl = escapeProperly(window.location.href);
   4:         var url_array = window.location.href.split("/");
   5:         var lastelementindex = url_array.length -1
   6:         var pagename = url_array[lastelementindex];
   7:  
   8:         STSNavigate("/pagenotfound/smart404.aspx?oldUrl=" + requestedUrl +"&k=" + pagename);
   9:     
</script> </head> <body> </body> </html>

 

3) Create a custom 404 page. In our case this page will require a code behind because we need to support some vanity redirect logic. Additionally this page should contain the supporting webparts to provide search results based on query string parameters passed in from the custom 404 page. This page is being provisioned as a site page as opposed to an application page in the layouts folder so that it can support customization per web application. Because this page will need to support cusomtization we do not want to do inline code.

 

<asp:Content ID="Content1" ContentPlaceholderID="PlaceHolderPageTitle" runat="server">
    404 - Page Not Found
</asp:Content>

<asp:Content ID="Content3" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
    </asp:Content>

<asp:Content ID="Content2" ContentPlaceholderID="PlaceHolderMain" runat="server">
    <div class="smart404">
        <h2>
            The page you requested could not be found.</h2>
        <div class="smart404Notice">
            We apologize, but the page you requested is unavailable. It may be temporarily offline
            due to maintenance, or it may not exist.
        </div>
        <div class="smart404Return">
            <a href="javascript:history.go(-2)">Return to Previous Page</a>
        </div>
        <div class="smart404Search">
            <h4>Best Bets</h4>
            <WebPartPages:WebPartZone runat="server" ID="mainContentZone">
            <ZoneTemplate>
                 <Search:coreresultswebpart runat="server" id="search" DisplayRSSLink="false" DisplayAlertMeLink="false"
                        ResultsPerPage="10" ChromeType="None" >
                    </Search:coreresultswebpart>
                    <br />
                    <Search:SearchPagingWebPart runat="server" id="searchpaging" ChromeType="None">
                    </Search:SearchPagingWebPart>
            </ZoneTemplate>
            </WebPartPages:WebPartZone>
        </div>
    </div>
       
</asp:Content>

 

4) Next we'll need to provision the custom 404 page and the location it will be residing in:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  

  <ListInstance TemplateType="101" RootWebOnly="false" 
              Id="PageNotFound"
              Title="Page Not Found"
              Description="Page Not Found Supporting Pages"
              Url="PageNotFound"
              OnQuickLaunch="False"
              FeatureId="00BFEA71-E717-4E80-AA17-D0C71B360101">
  </ListInstance>
          

  <Module Url="PageNotFound"
          Path="Pages"
          RootWebOnly="TRUE">
    <File Url="Smart404.aspx"
          Name="Smart404.aspx"
          Type="GhostableInLibrary"
          IgnoreIfAlreadyExists="TRUE">   
    </File>
  </Module>

</Elements>

 

5) In the feature receiver for our feature we want to make sure that the new HTML form gets registered.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{

   string webAppUrl = (properties.Feature.Parent as SPSite).Url;

   SPWebApplication webApplication = SPWebApplication.Lookup(new Uri(webAppUrl));

   webApplication.FileNotFoundPage = "smart404.html";

   webApplication.Update();

}

 

In part 2 of my next post I'll touch on the implementation of the vanity portion of the smart 404 implementation.

Published: Jun-08-08 | 4 Comments | 17 Links to this post

Jun08

I felt naked without Visual Studio today...

Seriously I felt naked without Visual Studio in today's presentation at InBetween down here in Orlando. I've been giving SharePoint developer talks for a few years now but this was the first presentation I have given on SharePoint administration. The slide deck really needed some more love but I'm very comfortable with the topic so I thought that would help out things but once I got in front of the crowd I felt like I didn't have anything to do. Just PowerPoint and some admin screens. Hopefully it wasn't as painful for the crowd as it was for me lol. I think I'm going to stick with my developer discussions while I work on my technique for non-code presentations.

Published: Jun-08-08 | 0 Comments | 1 Link to this post

Jun04

How do you provision a MasterPages across all SharePoint sites from a single Feature?

This has came up twice this week with completely different clients so I thought this would be a good candidate for a post.  The answer is with an intelligent feature receiver. The scenario is that a client is running Windows SharePoint Services and would like to provision a new masterpage and stylesheet through a feature and set that as the default masterpage and stylesheet for not only the current site but all sites below that as well.

Step 1: Create a new feature that will provision our masterpage and stylesheets and reference a receiver class that will apply our logic.

<Feature xmlns="http://schemas.microsoft.com/sharepoint/"
         Id="4344610E-2C91-4ca0-B9DD-89CBA2168545"
         Title="Global MasterPage"
         Description="Global MasterPage"
         Scope="Web"
         Hidden="False"
         Version="1.0.0.0"
         ReceiverAssembly="GlobalMasterPage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=[Insert your key]"
         ReceiverClass="GlobalMasterPage.MasterpageFeatureReceiver">
  <ElementManifests>
    <ElementManifest Location="elements.xml" />
    <ElementFile Location="MasterPages\custom.master" />
    <ElementFile Location="Styles\custom.css" />
  </ElementManifests>

</Feature>

 

Step 2: Create a new elements file that will provision the masterpage, create a new instance of the CSS style library, and provision the stylesheet in the new library

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

  <!-- add master page(s) -->
  <Module Url="_catalogs/masterpage"
          Path="MasterPages"
          RootWebOnly="false">
    <File Url="custom.master"
          Name="custom.master"
          Type="GhostableInLibrary">
    </File>  
  </Module>

 <!-- Provision the list for the style sheet-->
  <ListInstance TemplateType="101" RootWebOnly="false"
                   Id="CSSStyleLibrary"
                   Title="CSSStyleLibrary"
                   Description="Styles"
                   Url="CSSStyleLibrary"
                   OnQuickLaunch="False"
           FeatureId="00BFEA71-E717-4E80-AA17-D0C71B360101">
  </ListInstance>

  <!-- Provision the style sheet-->
  <Module Url="CSSStyleLibrary" Path="Styles" RootWebOnly="false">
    <File Url="custom.css" Name="custom.css" Type="GhostableInLibrary">
    </File>
  </Module>

</Elements>

 

Step 3: Create a new assembly that contains the feature receiver. Don't forget this assembly needs to be deployed to the GAC. This receiver sets the default masterpage and style sheet for the current site and then activates the same feature on child sites. This has the affect of recursively applying the masterpage and stylesheet to each and every site.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
     SPWeb web = properties.Feature.Parent as SPWeb;

     web.MasterUrl = "/_catalogs/masterpage/custom.master";
     web.AlternateCssUrl = "/sytles/custom.css";
     web.Update();

     foreach (SPWeb subWeb in web.Webs)
     {
         if (subWeb.Features[new Guid("4344610E-2C91-4ca0-B9DD-89CBA2168545")] == null)
         {
             subWeb.Features.Add(new Guid("4344610E-2C91-4ca0-B9DD-89CBA2168545"));
         }

     }

}

 

Naturally your feature will need to include the proper masterpages and stylesheets. And the GUID that defines the feature to be activated should match the GUID of your feature. In the end your Visual Studio solution should look something like:

image

 

From a management standpoint as long as you don't customize either the masterpage or css with SPD (meaning you manage the changes in the Feature on the file system) all changes will be visible on any site using this feature. Seeing that this came in handy twice in one week hopefully it will come in handy for some of you folks out there.

Published: Jun-04-08 | 0 Comments | 20 Links to this post

Jun02

Triangle .Net User Group - SharePoint Special Focus Group Kick-Off Meeting July 1st

UPDATE: The date of this event has been changed to July 1st. Please update your calendars.

I'm very excited to announce the formal kick-off meeting of TRINUG's SharePoint Special Focus Group next month on June 24th. In this monthly special focus group we will be emphasizing the developer aspects of SharePoint but any and all topics in SharePoint and related technologies will be fair game.  From time to time I plan to have some scheduled speakers, including SharePoint MVPs drop by to give us some out of town perspectives on SharePoint topics. This will be a very interactive and social event. Bring your ideas, thoughts, questions along with you. This is open to all who have an interest in learning more about SharePoint whether you've been working with it for years, just got it thrown on your lap, or you're just curious about what the buzz is all about.

In this first session we're going to be covering the basics of Features. Features, although poorly named, are one of the basic building blocks of SharePoint and are essential for provisioning functionality in your site.  Without a solid understanding of Features you'll never move past SharePoint Designer for developing robust, reusable, and arguably more interesting SharePoint components.

I would like to give a special thanks out to Mark Weinburg over at Tek Systems for providing the facilities for this event. I walked through the conference room the other day and its going to be a great location for the meeting!

Because of the level of interest that has been expressed in the last few months we will be requiring registration for this first event to make sure we don't go over capacity. Registration should be setup at http://www.trinug.org in the next couple days and we'll provide additional information such as time and driving directions. Seating will be limited to 30. In all honesty I'm not expecting that many folks and I like to keep the group small and interactive but if we do have more folks then that I'll start working on some different outlets for the SharePoint community in Raleigh.

Drop me some comments if you have some ideas on topics you'd like to see or general questions\feedback. I look forward to seeing you all!

Published: Jun-02-08 | 0 Comments | 20 Links to this post

 Next >>