Friday, April 25, 2008

Adding a custom content type to a site definition

Today we'll be a adding a custom content type to our site definition we created in this post. The project that we're working with is an STSDEV project, so my project file structure reflects that. If you aren't using STSDEV you may need to make a few modifications. If you don't know what custom content types are, here's a good overview.

The content type we develop here will extend the Document content type. We'll call it a Project Proposal. Now let's get to work.

Step 1: Project Set up

Aside from basing this project on the site definiton I created earlier, we'll need to add the Feature folders that reflect the structure on the server.
  1. Right click on the TEMPLATE folder in your project and choose 'Add New Folder', name this folder 'FEATURES'.
  2. Then add a folder under 'FEATURES' called 'ProjectProposalContentTypes'.

Step 2: The feature definition file

Since we want to deploy this as a feature we'll need to create the feature definition file.
  1. Right click the 'ProjectProposalContentTypes' folder and 'Add a New Item'. Choose XML document and name it 'feature.xml'.
  2. Paste the following code into the 'feature.xml' file:

    <?xml version="1.0" encoding="utf-8" ?>
    <!-- _lcid="1033" _version="12.0.4017" _dal="1" -->
    <!-- _LocalBinding="" -->
    <Feature Id="BC91162B-EB3D-4412-BBE0-E5547CAB05CA"
    Title="Project Proposal Content Types"
    Description="Project Proposal Content Types"
    Version="1.0.0.0"
    Scope="Site"
    xmlns="http://schemas.microsoft.com/sharepoint/">
    <ElementManifests>
    <ElementManifest Location="contentTypes/columns.xml" />
    <ElementManifest Location="contentTypes/ctypes.xml" />
    </ElementManifests>
    </Feature>

    There are a few things to note:
    • You should put your own GUID into the feature 'ID' attribute (you can generate a GUID from the tools menu in Visual Studio, just make sure to use Registry format and delete the curly brackets)
    • In the <ElementManifests> node there are two <ElementManifest> nodes that refer to files that we haven't created yet. The locations indicate that they will be in the 'contentTypes' folder which will be in the same directory as the feature.xml file.

Step 3: The columns.xml file (The columns/'Elements' file)

To keep things organized, we'll be defining the columns in a separate elements file from the content type definition. We'll then refer to the columns from the content type definition file in the next step.

  1. Right click on your 'ProjectProposalContentTypes' folder and add a new XML document called 'columns.xml'
  2. Add the following xml to your 'columns.xml' file:

    <?xml version="1.0" encoding="utf-8" ?>
    <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <Field ID="7F80D077-A4FF-4ad0-8F21-432957A6E39C"
    Name="ProjectType"
    StaticName="_ProjectType"
    Group="Project Proposals"
    DisplayName="Project Type"
    Type="Choice"
    Required="True"
    Sealed="TRUE">
    <CHOICES>
    <CHOICE>General Software Development Project</CHOICE>
    <CHOICE>Web Development Project</CHOICE>
    <CHOICE>Systems Architecture Project</CHOICE>
    </CHOICES>
    </Field>
    <Field ID="DED5654C-975C-4278-B198-978CE295454A"
    Name="TimeEstimate"
    StaticName="_TimeEstimate"
    Group="Project Proposals"
    DisplayName="Time Estimate (in hours)"
    Type="Number"
    Format="FALSE"
    Required="True"
    Sealed="TRUE"
    />
    <Field ID="AB47551C-CF14-4823-849A-4BB6E834A3E7"
    Name="Rate"
    StaticName="_Rate"
    Group="Project Proposals"
    DisplayName="Rate (/hr)"
    Type="Currency"
    />
    </Elements>

    What does this all mean? The Field 'ID' attribute uniquely identifies each field with a GUID. The 'Name' attribute is the name of the field. The 'StaticName' is the internal name of the field (used when using the API to get the field value). 'Group' is the column group that the column belongs to. The 'DisplayName' attribute is the column name as it appears in the UI. The 'Type' attribute is the data type that is contained within the field. ALSO NOTE: I broke out the custom Field Definitions into a separate file (above), but if you wanted to organize your project differently you could put your field definitions into the ctypes.xml file.
Step 4: The ctypes.xml file (the content type definition element file)

This file is the main content type definition file. We'll add the fields necessary for this content type. We'll be including 2 kinds of Field References: one for the built in fields that come with SharePoint and one that refers to the fields we defined in the step 3, above.
  1. Right click on your 'ProjectProposalContentTypes' folder and add a new XML document called 'ctypes.xml'
  2. Paste the following code into your 'ctypes.xml' file:

    <?xml version="1.0" encoding="utf-8" ?>
    <Elements xmlns="http://schemas.microsoft.com/sharepoint/" >
    <ContentType ID="0x010100817535AA5D8F4d47BC9AAE0507E045E7"
    Name="Project Proposal Content Type"
    Group="List Content Types"
    Description="A content type for managing project proposals"
    Version="0"
    Hidden="FALSE"
    >
    <FieldRefs>
    <FieldRef ID="fa564e0f-0c70-4ab9-b863-0177e6ddd247" Name="Title"/>
    <FieldRef ID="52578FC3-1F01-4f4d-B016-94CCBCF428CF" Name="_Comments"/>
    <FieldRef ID="7F80D077-A4FF-4ad0-8F21-432957A6E39C" Name="ProjectType" />
    <FieldRef ID="DED5654C-975C-4278-B198-978CE295454A" Name="TimeEstimate" />
    <FieldRef ID="AB47551C-CF14-4823-849A-4BB6E834A3E7" Name="Rate" />
    </FieldRefs>
    </ContentType>
    </Elements>

    Things to note:
    • The ContentType ID attribute is the string "0x010100" followed by a GUID (with no dashes. This leading "0x010100" is important. (It specifies that we'll be extending a document content type) This number breaks down as follows (from left to right). 0x = system, 0x01 = item, 0x0101 = document. The final '00' before the GUID is a spacer.
Step 5: Test it out.

Make sure your build configuration is set for DebugDeploy (this is an STSDEV setting) and build the project. After the solution has built correctly, you should be able to go to your SharePoint site, choose Site Actions > Site Settings > Site Collection Features and activate your feature. Then you can see your new content type in the 'Site Content Types' area. Note that this content type is based on the 'Document' content type.

If you'd like to apply this content type to a list. Create a new document library. Go to the list settings for that document library and under Advanced Settings choose to enable content type manangement. Then, back on the list settings for that document library, choose to add a content type from the existing site content types. You should see your content type listed there.

Step 6: Add the content type to your site definition automatically

The steps described above basically explain how to create a Custom Content Type and deploy it as a feature, but since I'm building off my original project (if you haven't done it and want to keep up, go here) I want this feature to be automatically associated with my site definition I'm creating. Here's how:

  1. Open your onet.xml for the site definition
  2. In the <Configurations><Configuration><SiteFeatures> tag, add a <Feature ID='insert your feature id here' /> (if you scoped your Custom Content Type feature at the 'Web' level, you'd put the Feature tag into the <WebFeatures> tag (we had to scope at the 'Site' level because the feature is defining Fields).
  3. That's all you should have to do to get your custom feature auto activated on site creation.
  4. Now do a DebugDeploy (or ReDeploy) and test it out by creating a new site collection based on the site definition and checking the Site Settings page to ensure the Site Feature is activated.

Thursday, April 24, 2008

Creating and Deploying a Custom Master Page with STSDev

This is my second post on creating SharePoint solutions the easy way, with STSDEV. This time around I'll be enhancing my last post's project by adding a custom master page. This master page will start by using a base master page from Heather Solomon's site. If you don't already have it, download it.

The base master page simply gives you a stripped down master page that you can enhance. We'll actually do some enhancements to give a taste of the power that you have over the UI when you choose to customize a master page.

Step 1: Add the base master page to the project
  1. If you haven't already done so, download and unzip the base master page from Heather Solomon's site.
  2. Right click on your 'RootFiles\TEMPLATE\SiteTemplates\YourSiteTemplateName' directory and choose 'Add Existing Item'
  3. Select the base master page from your file system and click 'Add'
  4. Rename the file 'SharePointersDemoCustom.master'.
Step 2: Editing the Default.aspx file
  1. Open the default.aspx file
  2. At the top of the page, change the 'Page' directives 'MasterPageFile' attribute to '~masterurl/custom.master' (note that this value can only be 'default.master' or 'custom.master' but you can name your file whatever you like because the onet.xml file's <Module> element contains the reference to the actual file)
  3. Save the file
Step 3: Editing the onet.xml file
  1. Open the onet.xml file
  2. In the <ListTemplates> node add the following child node:

    <ListTemplate Name="mplib" DisplayName="$Resources:MasterPageGallery;"
    Description="$Resources:global_onet_mplib_desc;"
    SetupPath="global\lists\mplib" Type="116"
    BaseType="1" Path="GLOBAL" Hidden="TRUE"
    HiddenList="TRUE" NoCrawl="TRUE" Unique="TRUE"
    Catalog="TRUE" OnQuickLaunch="FALSE" SecurityBits="11"
    AllowDeletion="False" AllowEveryoneViewItems="TRUE"
    Image="/_layouts/images/itdl.gif" AlwaysIncludeContent="TRUE"
    DocumentTemplate="100" />

  3. Find the <Configuration> node the 'ID' attribute of 0 (zero)
  4. Add the 'MasterUrl' attribute to the <Configuration> node like so:

    <Configuration ID="0" Name="Blank" MasterUrl="_catalogs/masterpage/SharePointersDemoCustom.master">

  5. The <Configuration> node you just modified should have a <Modules> child element. Add the following <Module> tag as a child of this node

    <Module Name="CustomMasterPage" />

  6. Now go to the outer <Modules> tag that is a child of the main <Project> node and add the following child <Module> node

    <Module Name="CustomMasterPage" List="116" Url="_catalogs/masterpage" RootWebOnly="FALSE">
    <File Url="SharePointersDemoCustom.master" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE" />
    </Module>
Step 4: Deploy the project

Again, STSDEV makes this a breeze. If this is the first time you've deployed this solution change your build configuration to DebugDeploy and do a build, otherwise do a DebugRedeploy.

Now create a new site based on your site definition and see if your masterpage is working correctly. (Note, it should look pretty ugly since we haven't modified the UI yet.

Step 5: Start tweaking that master page!

Here's a bunch of really good resources for customizing your master pages all in one place. Heather Solomon really is the expert when it comes to SharePoint branding:

http://www.heathersolomon.com/blog/articles/sp2007.aspx

Wednesday, April 23, 2008

Deleting sites that were created with errors

This post will have something to do with my next post...

When you are creating site definitions and deploying them via a solution, you may successfully deploy them and then find out there is a problem with them upon creation, i.e. you get an error message when creating a site using the definition through the UI.

After the error occurs, the site may actually have been created, but does not show up in your navigation bar and the root of the site will be inaccessible. Interestingly enough, you can still get to the site's settings page by going to: http://serverName/siteName/_layouts/settings.aspx

You can then delete the broken site from there.

Using STSDEV to create a solution with a Site Definition

In this post I'll give a basic overview of how to create a simple site definition using STSDEV. The purpose of using STSDEV is to give the project a standardized structure and to make use of the solution autogeneration.

Here are the steps (Make sure you've downloaded STSDEV and run through the tutorials, or understanding this might be tough)

Part 1: Generate the STSDEV solution

  1. Open Visual Studio and run STSDEV (should be an option on your tools menu if you ran through the tutorials, see above link)
  2. Choose to create an empty solution with C# assembly
  3. Click 'Create the Solution'
  4. After you create the solution, you'll need to open it from the file system. STSDEV does not automatically open it for you in Visual Studio.

Part 2: Set up the project structure

Even the most basic site definition needs at least 3 files: a webtemp.xml file, an onet.xml file, and a default.aspx file. So let's create these files in the appropriate places.
  1. right click on the 'RootFiles' folder and add a new folder called 'TEMPLATE'
  2. Under the 'TEMPLATE' folder you created add a new folder called '1033'
  3. Under the '1033' folder you created create a new folder called 'XML'
  4. In the 'TEMPLATE/1033/XML' folder add a new XML file called 'webtemp.YourSiteName.xml', where your site name is what you want to call your site.
  5. Under the 'TEMPLATE' folder you created add a new folder called 'SiteTemplates'
  6. Under the 'SiteTemplates' folder create a folder called 'Your Site Name', with the name of the site you'd like to use.
  7. In the 'Your Site Name' folder, create a folder called 'xml'
Following those steps should set up your directory structure.

Part 3: The webtemp*.xml file

According to the MSDN documentation: 'The WebTemp*.xml files contain the site definition configurations that are available on the Template Selection section of the New SharePoint Site page'. So the details of this file determine how your site definition information will show up on the 'Create New Site' page.

  1. Go to the WebTemp.xml documentation page and copy the xml code into your webtemp*.xml file.
  2. Delete all the <template> nodes except for the first one
  3. In the only remaining <template> node, delete all the <configuration> nodes except the first one
  4. Enter a name for the <template> node 'Name' attribute (ensure this is the same name as your directory name in the SiteTemplates folder)
  5. Enter an id for the <template> node 'ID' attribute (if you've never done this before, start with 11001, this is because certain ID ranges are reserved. You'll notice that most of the out of the box templates are in the 10000 range, so hopefully anything in the 11000 range will be ok).
  6. Give the <configuration> node ID attribute a value of '0' (zero)
  7. Give the <configuration> node Title attribute a title of your choosing.
  8. Give the <configuration> node Description attribute a description of your choosing.
  9. Give the <configuration> node DisplayCategory attribute a category of your choosing (choosing 'Development' is a good idea, this will be the name of the tab on the 'Create Site' page that your site definition appears under)
Part 4a: Copying the onet.xml and default.aspx pages.

Since I will only be talking about the minimum needed to get your simple site definition working, after you complete this (or before) you might want to read up on the documentation for onet.xml

Because the onet.xml and default.aspx files are fairly long I won't post it here. What I do is use the version that is generated from VSeWSS. If you don't have this, download it because it is pretty useful, and generates solutions for you as well. I actually used this all the time before STSDEV came out and I decided that STSDEV generated what I considered to be cleaner and more well structured solutions. I have a template directory that contains most of the files that I use that I just copy into my solutions as I create them. I'll give the instructions on how to get the onet.xml and default.aspx file into your solution if you have VSeWSS v1.1 installed.
  1. Right click on your solution node for your STSDev Project and choose Add > New Project
  2. Choose 'Blank Site Definition' and create it
  3. In the project expand the 'Site Definition' folder and you'll see an onet.xml file, copy this to the 'TEMPLATE/SiteTemplates/Your Site Name/xml' folder in your STSDEV project
  4. In the same 'Site Definition' folder you found the onet.xml file in, you'll see a default.aspx file, copy this to the 'TEMPLATE/SiteTemplates/Your Site Name'.
Part 4b: Modifying the onet.xml file

There are only a few changes you need to make to the onet.xml file.
  1. Set the <project> node's 'Title' attribute to whatever you like.
  2. Set the <project> node's 'Revision' attribute to '1'.
  3. Set the <configuration> node's (under <project>) 'ID' attribute to '0' (zero).
Part 5: Deploy your solution

This is where STSDEV really shines, you basically don't need to do anything to get your site deployed other than select the 'DebugDeploy' configuration and do a build. If you did everything correctly you should be able to test your deployment by going to your site collection's home page and choosing Site Settings > Create and create a new site using your template.


Monday, April 21, 2008

Security issue when calling a WCF service from SharePoint

I've recently been charged with integrating several WCF services into SharePoint and it's been relatively smooth sailing up until I began to receive the following exception:

"The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate......."

My endpoint configuration originally looked like so:


<endpoint address="http://myserver/Services/BackOfficeService/basic"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IBackOfficeService"
contract="BackOfficeService.IBackOfficeService" name="BasicHttpBinding_IBackOfficeService" />

Looks ok, right?

Wrong! We are missing the 'identity' child of the endpoint element. So, to correct this issue, the final product should look like this:

<endpoint address="http://myserver/Services/BackOfficeService/basic"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IBackOfficeService"
contract="BackOfficeService.IBackOfficeService" name="BasicHttpBinding_IBackOfficeService">
<identity>
<userPrincipalName value="service@company.com" />
</identity>
</endpoint>

HTH,

Grant

Wednesday, April 16, 2008

STSDEV is pretty awesome, but...

I've begun using STSDEV as the start for my projects instead of VSeWSS. I like the simplicity of it, especially as Ted Pattison says in his first of three screencasts that you can load it into any installation of Visual Studio (that corresponds to the project type you created) because you don't rely on Add-ins/Extensions.

After running through the screencasts, I decided it was time to do the solution release build and I ran into immediate trouble with a "ERROR: Could not find file DeploymentFiles\SoulutionPackage.ddf ..." error message in the Output Window.

The solution, found on the STSDEV discussion board, is as follows:


  1. Modify the "ReleaseBuild" target in the DeploymentFiles\Microsoft.SharePoint.targets file as below (fix highlighted in bold red):

    <target name="ReleaseBuild">
    <message text="Deleting Pacakge File..." importance="high">
    <delete files="$(ProjectDeploymentFilesFolder)\$(PackageFile)" continueonerror="true">
    </delete>

    <message text="Building Cab File (Release Version)" importance="high">
    <exec command="$(MAKECAB) /F $(ProjectDeploymentFilesFolder)\SolutionPackage.ddf /D CabinetNameTemplate=$(PackageFile)" continueonerror="false">
    </exec>
    </message>
    </message>
    </target>



  2. Rebuild with the "DebugBuild" configuration
  3. Restart Visual Studio (to ensure nothing was cached)
  4. Rebuilt with the "ReleaseBuild" configuration

If you modify the Release Build configuration in the Microsoft.SharePoint.targets file right when you open the project for the first time after creating it in STSDEV it'll save you having to do the above steps.

ASP.Net AJAX and SharePoint

There's a project on CodePlex that integrates ASP.Net AJAX with SharePoint:

I've only read the overview, but I've had lots of projects that I've wanted to use AJAX with SharePoint. So I can see how this will be a tremendous help.

http://www.codeplex.com/sharepointajax