Quicknote: Forcing uninstall of msi

I have recently been working on a build server that compiles and installs an MSI. However, uninstall timed out on one occasion. When I subsequently tried to uninstall through add / remove programs, I got the error “The installed product does not match the installation source”. Fortunately, I was able to use the following command to force uninstall using another version of the msi.

msiexec /i myinstaller.msi REINSTALLMODE=vomus REINSTALL=ALL

SharePoint: Adding a webpart to a page through the object model

The code below illustrates adding a custom webpart to a webpart page (it doesn’t have to be a custom webpart, you can also following this approach with standard webparts). In this case I am going to add a custom redirector webpart to redirect to ./home/default.aspx to the left webpart zone on Default.aspx.

using System;
using System.Text;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;
using MyWebParts.Redirector;
...
public static void AddTestWebPartToDefaultPage(string webUrl)
{
    using (SPSite site = new SPSite(webUrl))
    {
        using (SPWeb web = site.OpenWeb())
        {
            string webPartPageUrl = string.Format("{0}/home/default.aspx", web.Url);
            Redirector homePageRedirector = new Redirector();
            homePageRedirector.Destination = webPartPageUrl;
            SPLimitedWebPartManager webPartManager = web.GetLimitedWebPartManager("default.aspx", PersonalizationScope.Shared);
            webPartManager.AddWebPart(homePageRedirector, "Left", 0);
        }
    }
}

SharePoint: Stopping redirect to access denied page

If a web is accessed that the current user doesn’t have permissions to, SharePoint’s default behaviour is to redirect you to the “access denied” page. This is true if you are trying to access it through the object model as well, even if you try to catch the System.UnauthorizedAccessException.
The workaround to this is to set the property CatchAccessDeniedException on the SPSite object to false. Once this is done, you can simply catch the unauthorised exception in your code. I.e.
using (SPSite siteCollection = new SPSite(“http://localhost/sites/blah”))
{
    siteCollection.CatchAccessDeniedException = false;
    try
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            // Attempt something here       
        }       
    }
    catch (System.UnauthorizedAccessException)
    {
        // Handle error
    }
}
Ryan Rogers has posted a good article on this at here on this (although it refers to the old version of SharePoint, it still works on MOSS / WSS 3.

SharePoint: Content Type IDs

Content type ids can be found out by look at the ctypes feature. Standard MS ones are typically of the form 0x01XX (where X is a hex digit). Note that content types can inherit. Inheritance is specified by the the first part of the content type id (i.e. the content type id begins with the id of its parent and typically appends two hexidecimal digits to this.

Note that when creating content types, it is best to do this in the elements.xml file of a feature. Note that your content type will inherit properties from whatever class is specified. It is worth noting that you can specify a new content type in a schema.xml file of a list feature (aka list definition), however inheritance from the parent class is not preserved if specified there (bizarrely).

Content type ids can actually be formed in two ways:

  1. as above, adding two hex digits to the parent content type
  2. by appending “00” then a guid without dashes or brackets

This msdn article gives more info http://msdn2.microsoft.com/en-us/library/aa543822.aspx.

MOSS / WSS 3.0: Creating a custom list feature

Rough guide to creating a custom list feature (aka a list definition).

Creating our list feature

  1. Copy an existing feature e.g. 12\Features\ContactsList to a new feature folder such as 12\Features\SkypeContacts (where SkypeContacts is the name of the new feature).
  2. In the new feature folder, edit the feature.xml file changing the following:
    • Create a new guid for the feature (via vs2005 tools menu). Remember to strip out braces when putting in
    • Replace title and description (note that you are supposed to refer to a resource to aid globalisation, but I personally didn?t bother (for the time being) and simply replace with strings.
    • Leave everything else as is (including the element manifest node). Your feature.xml file should look similar to this:

<?xml version="1.0" encoding="utf-8"?>
<Feature Id="2C3A48D0-9FF2-487b-9F02-B5C2A8D42261"
    Title="Skype Contacts List"
    Description=" Skype Contacts List, used to store skype contacts."
    Version="1.0.0.0"
    Scope="Web"
    Hidden="TRUE"
    DefaultResourceFile="core"
    xmlns="http://schemas.microsoft.com/sharepoint/">
    <ElementManifests>
        <ElementManifest Location="ListTemplatesContacts.xml"/>
    </ElementManifests>
</Feature>

Altering the element manifest

Go the the elementmanifest (i.e. ListTemplates\Contact.xml in our example) and make the following changes:

  1. For the list template node, change the type attribute (it is recommended that you use a number over 10000, as low numbers are reserved by Microsoft).
  2. Change the title and description attribute (note that you are supposed to refer to a resource to aid globalisation, but I personally didn?t bother (for the time being) and simply replaced with strings.
  3. Note that you should leave the name as it is (i.e. contacts in our example) as this seems to be used to reference the subfolder of our feature that contains the schema. You xml file should look similar to this:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <ListTemplate
        Name="contacts"
        Type="40006"
        BaseType="0"
        OnQuickLaunch="TRUE"
        SecurityBits="11"
        Sequence="330"
        DisplayName="Skype Contacts List"
        Description="Contacts list for recording my skype contacts."
        Image="/_layouts/images/itcontct.gif"/>
</Elements>

Adding an extra field to our list feature

Edit the schema.xml file. Make the following changes.

  1. In the fields section add a new field (my example creates a text field called SkypeId). Create a new guid for the id of this field. <Field ID=”{BD387A0C-A1C2-405e-A48B-234CC0CE6828}” Name=”SkypeId” DisplayName=”SkypeId” Type=”Text” SourceID=”http://schemas.microsoft.com/sharepoint/v3″ StaticName=”SkypeId”><!– _locID@DisplayName=”camlid9″ _locComment=” ” –></Field>
  2. In any views you want the field to appear (i.e. within the ViewFields node, add the field e.g. <FieldRef Name=”SkypeId” />
  3. Important: so that the field is available on the add / edit pages, you need to alter the contents types used by your list (altering field attributes in schema.xml is not sufficient). If your custom list feature is based on another list feature (as per our example) you have three options:
    • All Microsoft pre-defined content types are stored in a feature named ctypes. You can simple edit the ctypeswss.xml file that defines the content types and add your fields to the relevant content type (if you look at the ContentTypes node in the schema.xml file for your list feature you can determine which one needs to be altered). Note that if you alter this file directly, you are carrying out and unsupported modification and may be prone to problems if Microsoft release services packs etc. Note that when adding a field to a content type, the guid of that field should match that used in schema.xml.
    • Second approach is to create a feature that contains your new content type (simple copy from ctypeswss.xml and alter accordingly). You can then set a reference to this in the ContentTypeRef node in schema.xml
    • The third (and my preferred approach) is to copy the appropriate existing content type definition from ctypeswss.xml to the schema.xml. You should replace the content type node with the what it references in ctypeswss.xml. In our example, our schema.xml before altering the content type is: <ContentTypes>
            <ContentTypeRef ID="$Resources:core,ContactCTID;">
              <Folder TargetName="Contact" />
            </ContentTypeRef>
            <ContentTypeRef ID="0x0120" />
      </ContentTypes>

      EDIT: I would recommend that you create a content type in a seperate feature (adding the content type to the elements.xml file). This way you can inherit from other content types. Please see my article http://www.rnowik.com/blogpost/161/ for pointers on how to do this and how to set up necessary content type ids.
      Shown below is the ContentTypes node after the content type has been copied across and the additional field has been added to the content types (Note that if you create new content type, you need to create a new content type id (hex ? in my example my content type id is Ox1DD). <ContentTypes>
      <ContentType ID="0x01DD"
      Name="SkypeContact"
      Group="$Resources:List_Content_Types"
      Description="ShareWorkz Content Type"
      Version="0"
      >
      <FieldRefs>
      <FieldRef ID="{82642ec8-ef9b-478f-acf9-31f7d45fbc31}" Name="LinkTitle" DisplayName="$Resources:core,Last_Name;" Sealed="TRUE"/>
      <FieldRef ID="{bc91a437-52e7-49e1-8c4e-4698904b2b6d}" Name="LinkTitleNoMenu" DisplayName="$Resources:core,Last_Name;" Sealed="TRUE"/>
      <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" DisplayName="$Resources:core,Last_Name;" Sealed="TRUE"/>
      <FieldRef ID="{fdc8216d-dabf-441d-8ac0-f6c626fbdc24}" Name="LastNamePhonetic" Hidden="$Resources:core,True_Unless_Jpn" Required="FALSE"/>
      <FieldRef ID="{4a722dd4-d406-4356-93f9-2550b8f50dd0}" Name="FirstName" />
      <FieldRef ID="{ea8f7ca9-2a0e-4a89-b8bf-c51a6af62c73}" Name="FirstNamePhonetic" Hidden="$Resources:core,True_Unless_Jpn"/>
      <FieldRef ID="{475c2610-c157-4b91-9e2d-6855031b3538}" Name="FullName" />
      <FieldRef ID="{fce16b4c-fe53-4793-aaab-b4892e736d15}" Name="Email" />
      <FieldRef ID="{038d1503-4629-40f6-adaf-b47d1ab2d4fe}" Name="Company" />
      <FieldRef ID="{034aae88-6e9a-4e41-bc8a-09b6c15fcdf4}" Name="CompanyPhonetic" Hidden="$Resources:core,True_Unless_Jpn"/>
      <FieldRef ID="{c4e0f350-52cc-4ede-904c-dd71a3d11f7d}" Name="JobTitle" />
      <FieldRef ID="{fd630629-c165-4513-b43c-fdb16b86a14d}" Name="WorkPhone" />
      <FieldRef ID="{2ab923eb-9880-4b47-9965-ebf93ae15487}" Name="HomePhone" />
      <FieldRef ID="{2a464df1-44c1-4851-949d-fcd270f0ccf2}" Name="CellPhone" />
      <FieldRef ID="{9d1cacc8-f452-4bc1-a751-050595ad96e1}" Name="WorkFax" />
      <FieldRef ID="{fc2e188e-ba91-48c9-9dd3-16431afddd50}" Name="WorkAddress" />
      <FieldRef ID="{6ca7bd7f-b490-402e-af1b-2813cf087b1e}" Name="WorkCity" />
      <FieldRef ID="{ceac61d3-dda9-468b-b276-f4a6bb93f14f}" Name="WorkState" />
      <FieldRef ID="{9a631556-3dac-49db-8d2f-fb033b0fdc24}" Name="WorkZip" />
      <FieldRef ID="{3f3a5c85-9d5a-4663-b925-8b68a678ea3a}" Name="WorkCountry" />
      <FieldRef ID="{a71affd2-dcc7-4529-81bc-2fe593154a5f}" Name="WebPage" />
      <FieldRef ID="{9da97a8a-1da5-4a77-98d3-4bc10456e700}" Name="Comments" />
      <FieldRef ID="{BD387A0C-A1C2-405e-A48B-234CC0CE6828}" Name="HelloWorld" />
      </FieldRefs>
      </ContentType>
      <ContentTypeRef ID="0x01BA">
      <Folder TargetName="Contact" />
      </ContentTypeRef>
      <ContentTypeRef ID="0x0120" />
      </ContentTypes>

Deploying our list feature

To deploy and activate the feature, you need to run the following commands (note that as lists have web scope you will need to activate on each web that you want to use it):

  1. Stsadm ?o installfeature ?name [name of feature]
  2. Stsadm ?o activatefeature ?id [guid from feature.xml] ?url [url of web to deploy to]

Alternatively, you can create a custom site definition and reference our list by adding the following node the appropriate configurations WebFeatures node: <Feature ID="2C3A48D0-9FF2-487b-9F02-B5C2A8D42261" />

Notes

  • Note: It is worth noting that all of the Microsoft lists are grouped together in one feature (.xml) so that a reference to each list feature does not need to be made in the site definition, hence why you don?t see a reference to each list definition in the standard Sharepoint site definitions.
  • Tip: remember to change xmlns:ows=”Microsoft Sharepoint” to xmlns=”http://schemas.microsoft.com/Sharepoint” to get intellisense when working on the xml files.
  • Note that in an ideal world, I would be able to create an event handler that provisions additional fields when a new list is added, however there are no list created events in MOSS / WSS3 (only field added / deleted / adding / deleting on a list).

Related References:

MOSS / WSS 3.0: Checking permissions against an SPWeb

For Sharepoint 2003, I used to check a users rights by getting the user from the SPWeb.AllUsers collection (as this contains all users of groups as well as explicitly named users, unlike SPWeb.Users). I would have then tested whether a user was in a role using something like SPRole SPWeb.Roles[“rolenametogohere”].Users.

In MOSS / WSS 3, you can instead use the following code:

public bool UserHasPermission(string username, string role, string webAbsoluteUrl)
{
    using (SPSite siteColl = new SPSite(url))
    {
        using (SPWeb web = siteColl.OpenWeb())
        {
            SPBasePermissions requiredPerms = Web.RoleDefinitions[role].BasePermissions;
            return web.DoesUserHavePermissions(username, permsMask);
        }
    }
}

Username name is string e.g. nt\robnowik
Role is Sharepoint group e.g. “Conribute”
webAbsoluteUrl is path to web you want to test users permissions against e.g. http://localhost/sites/testsite/myweb/).

WSS 3: "Exception from HRESULT: 0x80040E14"

Time to kick-start the old blog again – it’s been a crazy couple of months, hence the lack of posts.
I had just configured a cruise control / msbuild build server to compile and install SharePoint code (on this box). Overnight however, windows update ran. The following error then started appearing in my build log (note that SharePoint sites and the site collection were created programmatically).

Starting Installation.....
01/11/2007 05:03:15: Creating Site Collection
01/11/2007 05:03:38: Exception from HRESULT: 0x80040E14

It turns out that this is because the content databases where not upgraded correctly. There is a knowledge base article at http://support.microsoft.com/kb/841216, however the suggested stsadm command did not work; I needed to alter it to the following: