Dedicated to development, configuration, and tips and tricks for both WSS 3.0 and MOSS

Posted by Brian LaSitis on Monday, 23 Mar 2009 03:38

Dan Zainea and Brian LaSitis, of Dell GICS, recently participated in the delivery of a SharePoint webcast organized by Quest Software, entitled “Streamlining Application Deployment with No Coding”.  This webcast was moderated by Joel Oleson, of Quest Software, and focused on the three primary methods of customization and development that are performed within a SharePoint environment:  Internet Explorer / Web UI site customizations, SharePoint Designer site customizations, and Visual Studio custom development artifacts.  The presentation aligned each type of customization with a set of goals applicable in a SharePoint environment, including maintainability, standardization, and flexibility, with the key takeaway being the selection of the right tool for the right solution.

In addition to Dell’s involvement in this presentation, demos were also given during it by Dustin Miller, SharePoint MVP of SharePoint Experts, and Curtis Kelly, Solutions Architect at Quest.  Dan and Brian, from Dell, organized the content for the third segment of the webcast, on the topic of Visual Studio developed customizations, and discussed the benefits of using SharePoint solution packages (WSPs).  The Dell discussion focused on the use of WSPs as a deployment tool for custom developed artifacts, and the benefits they offer by allowing a prescriptive process to be employed for moving customizations from development to production.

You may view the recording of this webcast at Quest’s web site, at http://www.quest.com/events/ListDetails.aspx?ContentID=9303.

Posted by Brian LaSitis on Wednesday, 4 Mar 2009 02:16

In my last posting in this series, we reviewed how to create a custom content type as a SharePoint feature.  While this is a great way to modularize the set of content types that are used within an organization, having them defined in a feature does not implicitly make them a part of all future sites that are created, as a SharePoint environment grows & evolves over time.   Let us briefly review a couple of approaches to get around this:

  • Define Procedures:  One approach to this is simply to define procedures, such that when new sites are created, the feature(s) that define the content types are activated as part of the process.  Unfortunately, while this method can be effective, it has room for error – if someone forgets to activate the features after a site is created, then that site is inconsistent, and over time you may end up with a lot of inconsistent sites within your organization’s SharePoint environment. 
  • Staple The Feature(s) to a Built-in Site Definition:  A second method would be to simply staple the custom features to one of the built-in site definitions that come with SharePoint, such as the Team Site or Blank Site.  While this would certainly ensure that the features were activated consistently, it might just be a little too consistent.  After all, if you have a feature that defines some content types intended for Client Project sites, and another feature that defines content types for Knowledge Base sites, which built-in site definition do you staple them to?  Do you draw straws to see which one will get attached to the Team Site and which one goes to Blank?  Do you staple all of our custom features to the Team Site definition & make them all available everywhere?  While these options work, and you might be able to tack on this work in a small 1-2 hour time window, they really don’t effectively leverage the SharePoint product capabilities.  You end up having all of these un-related features stapled on to the built-in site definitions, and everything is just one big hodgepodge.
  • Create a Custom Site Definition:  A third method is to bite the bullet and just create a copy of the built-in Team or Blank site definition, and include your custom feature in it directly, or leverage feature stapling to attach it.  This method, despite requiring one to face the challenge of a custom site definition, offers the best solution, as you could create multiple copies of the built-in Team site definition and tie different custom features to each, as needed, to ensure all.  Without question, this is the preferred approach of the three I’ve defined.

Creating a custom site definition based upon the built-in Blank site template is a relatively trivial task, so I will not belabor the process, but instead focus on how we can take our “copied” version, and modify it to include instantiation of our custom feature.

To get us all on the same page, I have updated my Visual Studio solution to include the additional necessary “12” hive folders and files for our new site definition, named ACME.  Below is what the updated structure looks like:

image

Also, for everyone’s reference, here is the contents of the custom WEBTEMP file that was created:

<?xml version="1.0" encoding="utf-8"?>
<!-- _lcid="1033" _version="12.0.4518" _dal="1" -->
<!-- _LocalBinding -->
<Templates xmlns:ows="Microsoft SharePoint">
    <Template Name="ACME" ID="11000">
        <Configuration ID="0" 
                       Title="Acme Blank Site" 
                       Hidden="FALSE" 
                       ImageUrl="/_layouts/images/blankprev.png" 
                       Description="An ACME blank site for you to customize based on your requirements." 
                       DisplayCategory="ACME" 
                       AllowGlobalFeatureAssociations="False" >    
        </Configuration>
    </Template>
</Templates>

Within the ONET.XML file, the only notable section is Configurations, shown below:

<Configurations>
    <Configuration ID="0" Name="AcmeBlank">
        <Lists />
        <Modules>
            <Module Name="DefaultBlank" />
        </Modules>
        <SiteFeatures>
            <!-- BasicWebParts Feature -->
            <Feature ID="00BFEA71-1C5E-4A24-B310-BA51C3EB7A57" />
            <!-- Three-state Workflow Feature -->
            <Feature ID="FDE5D850-671E-4143-950A-87B473922DC7" />
            <!-- Acme Content Types -->
            <Feature ID="A5DA673C-FF79-427c-BBB4-422937F82FCB" />
        </SiteFeatures>
        <WebFeatures>
            <Feature ID="00BFEA71-4EA5-48D4-A4AD-7EA5C011ABE5" />
            <!-- TeamCollab Feature -->
            <Feature ID="F41CC668-37E5-4743-B4A8-74D1DB3FD8A4" />
            <!-- MobilityRedirect -->
        </WebFeatures>
    </Configuration>
</Configurations>

As you can see, the Acme Content Types feature (A5DA673C-FF79-427c-BBB4-422937F82FCB) is included in the SiteFeatures section, meaning that this feature will be automatically activated when instances of this site definition are created.

Another approach that could be taken is instead of explicitly linking out to our custom feature from within the ONET.XML file, leverage feature stapling to instead attach our feature back to the site definition, using a TemplateName value of “ACME#0”, denoting configuration ID #0 of the ACME site definition.  This blog posting offers a nice explanation of what is involved in leveraging the feature stapling capability of MOSS 2007.  In all honesty, in this scenario, I’m not sure one method offers a significant advantage over the other.  I personally would probably take the route of just embedding the Feature instantiation within the ONET.XML, as my snippet from above illustrates, but as they say “de gustibus non est disputandum.”

At any rate, the whole process of creating my copy of the Blank Site Definition took maybe 30 minutes, and for the flexibility it offers to any sites created from it going forward, as well as the capabilities it opens up for being able to activate custom features that apply to the nature of the sites being created from it, this exercise is well worth it.  If nothing else, it can illustrate to a client that our skill level with the product is a couple of notches above the competition, and it positions them well for the future.

Posted by Brian LaSitis on Monday, 2 Mar 2009 07:23

In my last posting on this topic, I illustrated the use of a SharePoint feature to define a set of site columns to add to a given site.  In this posting, we will extend that same feature to include a second element manifest that will define a content type that uses these site columns.

If you recall, each of the site columns we defined included a unique ID value – these ID values will become important when we create a content type using these site columns.  Below is a table that references each site column we’ve created and the associated ID for each:

Site Column ID Name
{9A77BCEB-5230-48f2-93ED-D87DAAEC6998} ArticleHeadline
{CB2E757E-3876-40c8-A720-C98063DBCC3D} ArticleAbstract
{13480703-B260-4354-83DC-AA7AF22A629E} Classification
{B022435C-D299-40fd-B071-5C9DF95F6F4F} DisclosureLevels
{DBBD4D5F-A390-414b-9BB2-1160C1B2E35E} Lifetime
{7B6905FB-6619-4276-AB3D-9A04A04D8806} ExpectedCost
{6AAD913B-D930-49b5-8BCD-99FAB8857257} ExpiresOn
{D38E97DA-CD82-4a77-9086-3B97A6D61A92} Activated

 

In order to create a content type within SharePoint, you must inherit from an existing one.  In most cases, this inheritance will start with one of the built-in content types, such as Item, Document, or Folder.  For this exercise, we will create a content type that inherits from Document and includes the Article Headline, Article Abstract, Classification, Disclosure Levels, and Expires On custom site columns.  Consider the following XML that makes up our element manifest, named AcmeContentTypes.xml

<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <ContentType ID="0x0101007B9487E9C4334930A393D9E96E152D36" 
                 Name="Acme Document" 
                 Description="" 
                 Group="Acme Content Types" 
                 Sealed="FALSE" 
                 Version="0">
        <FieldRefs>
            <FieldRef ID="{9A77BCEB-5230-48f2-93ED-D87DAAEC6998}" Name="ArticleHeadline" />
            <FieldRef ID="{CB2E757E-3876-40c8-A720-C98063DBCC3D}" Name="ArticleAbstract" />
            <FieldRef ID="{13480703-B260-4354-83DC-AA7AF22A629E}" Name="Classification" Required="TRUE" />
            <FieldRef ID="{B022435C-D299-40fd-B071-5C9DF95F6F4F}" Name="DisclosureLevels" Required="TRUE" />
            <FieldRef ID="{6AAD913B-D930-49b5-8BCD-99FAB8857257}" Name="ExpiresOn" />
        </FieldRefs>
    </ContentType>
</Elements>

Let us step through what we have here, starting with the outer ContentType element.  This element is what defines the actual content type we are creating, and includes its ID, Name, Description, and Group.  Now you are probably looking at that ID value and going, huh?  Let us step back for a moment and discuss how an ID for a custom content type is created.

Content Type IDs

The ID value that is assigned to a custom content type defines two things -- (1) it is a unique identifier for the content type, and (2) it defines the inheritance tree, or lineage, for the content type.  The 1st point I’ve made makes complete sense, but the second probably sounds confusing – how could that ugly thing I’m calling an ID actually tell us *anything* about the inheritance tree for the content type.  Well, as it turns out, there is some madness to this.  Let us being with a definition for how a content type ID is constructed  -- there are two possible formats:

  • Parent Content Type ID + 2 Hexadecimal Values (cannot be “00”)
  • Parent Content Type ID + “00” + Unique Identifier

Let us also examine the ID values and hierarchy for the core set of content types included in SharePoint:

image

If you study that graphic, you can see that the most fundamental content type within SharePoint, named System uses the ID value of 0x.  The next level in defines the base Item content type, with an ID value of 0x01.  Let us look back at bullet point #1 in the rules for constructing a content type ID, and we can quickly see that the ID value of the Item content type makes sense, as it includes the ID value of its parent (System), and 2 hexadecimal digits.  As you go further into the hierarchy, you will see that this pattern continues nicely.  Now, let us look again at the ID value I included in our custom Acme Document content type, defined above, only this time, I will use some color coding to help us pick apart its components:

0x0101007B9487E9C4334930A393D9E96E152D36

  • 0x – System
  • 0x01 – Item
  • 0x0101 – Document
  • 0x0101007B9487E9C4334930A393D9E96E152D36 – Acme Document (note “00” separator)

Based upon the color coding, you can easily see that the ID of our custom content type does make sense, but you may be asking, why did I switch to the second method of creating a content type name, by using a unique identifier, instead of just two more hexadecimal digits?  The reason for doing so is a Microsoft recommendation, which states that when constructing a content type ID for a content type that is a direct child of a content type that was created by someone else, the method of using a unique identifier should be used.  This is to ensure that your custom content type will not conflict with any others that may have been created.  For any additional custom content types that are children of this one, the first method of just appending 2 hexadecimal digits can be used.  As an example, if we were to create a content type below our custom Acme Document content type, its identifier could be set to: 0x0101007B9487E9C4334930A393D9E96E152D3601 (note the addition of just “01” to our original ID value).

Hopefully I have done justice in explaining this concept, but if I did not, or you still have questions, please take a look at the following pages in MSDN, which I used as a reference in coming up with my explanation:

FieldRef Elements

Fortunately, the ID values for creating custom content types is probably the most challenging aspect of creating a content type, so once you are able to get your head around this logic, you should be in good shape.  Let us now proceed to what is included within our ContentType XML element, namely the set of site columns that our content type will define to augment the set of site columns that are “brought along” from the parent content type ours inherits from.  Each site column to be included is defined using a FieldRef XML element, which includes only two required attributes – ID and Name.  Fortunately these two are easy, as the ID value corresponds back to the ID value we defined for our custom site column, and the Name value also directly maps back to the site column definition.  Another attribute you may want to include in your FieldRef element is named Required, and accepts a Boolean value indicating whether the given site column will require a value when list item instances are created that use this content type. In my example, I have defined the Classification and Disclosure Levels fields as being required.

The only other remaining piece to allow us to complete our custom content type is to add a reference to our new element manifest to our feature.xml file.  Below is what our feature.xml file should look like with the additional reference added:

<?xml version="1.0" encoding="utf-8" ?>
<Feature  Id="A5DA673C-FF79-427c-BBB4-422937F82FCB"
          Title="Acme Content Types"
          Description=""
          Version="12.0.0.0"
          Scope="Site"
          Hidden="FALSE"
          xmlns="http://schemas.microsoft.com/sharepoint/">
    <ElementManifests>
        <ElementManifest Location="AcmeSiteColumns.xml" />
        <ElementManifest Location="AcmeContentTypes.xml" />
    </ElementManifests>
</Feature>

If we proceed to activate this feature on a site collection, we will see the following additional content type added, as shown below:

image

If we drill into the Content Type page for this entry, we can see the following details, identically reflecting what we defined in our custom feature:

image 

See just the same results you would get if you created this within the site itself using the Site Settings pages directly, only this method involves a LOT less pointing & clicking.

In the next posting in this series, we will see how we can take the custom feature we’ve developed here to create a content type, and tie it into a custom site template, so that as future site collections are created, they can each be consistent and include this content type by default.