Monthly Archives: August 2007

MOSS advanced search customization — Case does matter in advanced search XSLT

I don’t modify advanced search XSLT very often, so it seems like I’m climbing small hills every time.

My newest lesson is this: Case matters when referencing a column. In my advanced search, I have columns defined as this:

<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <
Columns>
    <
Column Name="GafTrainingInvoiceNumber" />
    <
Column Name="GafTrainingInvoiceLocation" />
    <
Column Name="WorkId"/>
    <
Column Name="Rank"/>
    <
Column Name="Title"/>
    <
Column Name="Author"/>
    <
Column Name="Size"/>
    <
Column Name="Path"/>
    <
Column Name="Description"/>
    <
Column Name="Write"/>
    <
Column Name="SiteName"/>
    <
Column Name="CollapsingStatus"/>
    <
Column Name="HitHighlightedSummary"/>
    <
Column Name="HitHighlightedProperties"/>
    <
Column Name="ContentClass"/>
    <
Column Name="IsDocument"/>
    <
Column Name="PictureThumbnailURL"/>
  </
Columns>
</
root>

The XLST that displays the invoice number and invoice location had been:

    <p>
      Training Invoice Number: <xsl:value-of select="GafTrainingInvoiceNumber"/>
      <
br></br>
      Training Invoice Location: <xsl:value-of select="GafTrainingInvoiceLocation"/>
    </
p>

However, the select has to reference the property in all lower case, as in:

    <p>
      Training Invoice Number: <xsl:value-of select="gaftraininginvoicenumber"/>
      <
br></br>
      Training Invoice Location: <xsl:value-of select="gaftraininginvoicelocation"/>
    </
p>

    
 

Until I corrected that, search results showed the labels (i.e. "Training Invoice Number") but no data.

 

MOSS: Functional Example – Custom Data Type

Business Scenario:

Enterprise-wide implementation of MOSS for manufacturing company with 30+ sites and a few dozen corporate departments.

Business Objective:

Despite a multitude of business groups (departments, locations, etc), certain data should be maintained at a global level. For example, an authoritative master list of all physical locations of the company (e.g. manufacturing facilities, warehouse locations, sales offices) should be maintained in a central location.

Technical Problem:

The enterprise taxonomy was implemented using multiple site collections. We would have liked to create the authoritative list of physical locations in a custom WSS list. Then, when we needed to have a column in a content type (or a column added to a list or doc library) that contained corporate locations, we would create a column using the "lookup" datatype and point to this master list.

Unfortunately, lookup datatypes must access a source list "locally" meaning that our authoritative list cannot span site collections.

Technical Solution:

Implement a new custom data type implemented based on SPField and represented as a DropDownList in the UI whose ListItems populate from the master WSS list.

We created a new site collection called "http://localhost/EnterpriseData". There, we created a custom list named "Corporate Locations". This list just uses the standard "Title" field to contain the list of actual corporate locations.

One follows several discrete steps to create a custom data type in WSS. They are:

  1. Define a class which inherits from SPField (one may inherit from other fields if required).

Here is the code for that:

    public class XYZZYCorporateLocationField : SPFieldText
    {
        public XYZZYCorporateLocationField
            (SPFieldCollection fields, string typeName, string displayName)
            : base(fields, typeName, displayName) { }

        public XYZZYCorporateLocationField
                    (SPFieldCollection fields, string displayName)
            : base(fields, displayName) { }

        public override BaseFieldControl FieldRenderingControl
        {
            get
            {
                BaseFieldControl control = new XYZZYCorporateLocationFieldControl();
                control.FieldName = this.InternalName;
                return control;
            } //get
        } // fieldrenderingcontrol

        public override string GetValidatedString(object value)
        {
            if (this.Required || value.ToString().Equals(String.Empty))
            {
                throw new SPFieldValidationException ("Department is not assigned.");
            }
            return base.GetValidatedString(value);
        } // getvalidatedstring

      } // XYZZYCorporateLocation

  1. Define another class that inherits from the base field control, as in:

    public class XYZZYCorporateLocationFieldControl : BaseFieldControl
    {
        protected DropDownList XYZZYCorporateLocationSelector;
        
        protected override string DefaultTemplateName
        {
            get
            {
                return "XYZZYCorporateLocationFieldControl";
            }
        } // DefaultTemplateName

        public override object Value
        {
            get
            {
                EnsureChildControls();
                return this.XYZZYCorporateLocationSelector.SelectedValue;
            } // get
            set
            {
                EnsureChildControls();
                this.XYZZYCorporateLocationSelector.SelectedValue = (string)this.ItemFieldValue;
            } // set
        } // override object Value

        protected override void CreateChildControls()
        {

            if (this.Field == null || this.ControlMode == SPControlMode.Display)
                return;

            base.CreateChildControls();

            this.XYZZYCorporateLocationSelector =
                (DropDownList)TemplateContainer.FindControl("XYZZYCorporateLocationSelector");

            if (this.XYZZYCorporateLocationSelector == null)
                throw new Exception("ERROR: Cannot load .ASCX file!");

            if (!this.Page.IsPostBack)
            {

                using (SPSite site = new SPSite("http://localhost/enterprisedata"))
                {
                    using (SPWeb web = site.OpenWeb())
                    {

                        SPList currentList = web.Lists["Corporate Locations"];

                        foreach (SPItem XYZZYCorporateLocation in currentList.Items)
                        {
                            if (XYZZYCorporateLocation["Title"] == nullcontinue;

                            string theTitle;
                            theTitle = XYZZYCorporateLocation["Title"].ToString();

                            this.XYZZYCorporateLocationSelector.Items.Add  
                                (new ListItem(theTitle, theTitle));

                        } // foreach

                    } // using spweb web = site.openweb()
                } // using spsite site = new spsite("http://localhost/enterprisedata")

            } // if not a postback

        } // CreateChildControls

    } // XYZZYCorporateLocationFieldControl

The above code basically implements the logic for populating the DropDownList with values from the WSS custom list located at http://localhost/enterprisedata and named "Corporate Departments".

I defined both classes in a single .cs file, compiled it and put it into the GAC (strong required, of course).

  1. Implement a control template (.ascx) as shown:

     

<%@ Control Language="C#" Inherits="Microsoft.SharePoint.Portal.ServerAdmin.CreateSiteCollectionPanel1,Microsoft.SharePoint.Portal,Version=12.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c"   compilationMode="Always" %>
<%
@ Register Tagprefix="wssawc" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<SharePoint:RenderingTemplate ID="XYZZYCorporateLocationFieldControl" runat="server">
  <Template>
    <asp:DropDownList ID="XYZZYCorporateLocationSelector" runat="server" />
  </Template>
</
SharePoint:RenderingTemplate>

The above is saved into c:\program files\common files\microsoft shared\web server extensions\12\controltemplates.

  1. Finally, we create an XML file to save into the …..\12\XML directory. This is CAML that defines our custom data type and for my example, looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<
FieldTypes>
  <
FieldType>
    <
Field Name="TypeName">CorporateLocations</Field>
    <
Field Name="ParentType">Text</Field>
    <
Field Name="TypeDisplayName">Corporate Locations</Field>
    <
Field Name="TypeShortDescription">All XYZZY Corporate locations including manufacturing or other facilities.</Field>
    <
Field Name="UserCreatable">TRUE</Field>
    <
Field Name="ShowInListCreate">TRUE</Field>
    <
Field Name="ShowInDocumentLibraryCreate">TRUE</Field>
    <
Field Name="ShowInSurveyCreate">TRUE</Field>
    <
Field Name="ShowInColumnTemplateCreate">TRUE</Field>
    <
Field Name="FieldTypeClass">Conchango.XYZZYCorporateLocationField, XYZZYCorporateLocationField, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b0b19e85410990c4</Field>
    <
RenderPattern Name="DisplayPattern">
      <
Switch>
        <
Expr>
          <
Column />
        </
Expr>

        <Case Value=""/>

        <Default>
          <
HTML>
            <![CDATA[
<span style="color:Red"><b>]]>
          </
HTML>
          
          <
Column SubColumnNumber="0" HTMLEncode="TRUE"/>

          <HTML><![CDATA[</b></span>]]></HTML>
          
        </
Default>
      </
Switch>
      
    </
RenderPattern>
  </
FieldType>
</
FieldTypes>
This XML file adds the custom data type to the WSS "library" and matches it up against the GAC’d assembly.

After moving all these bits into place, iisreset on the server and it should all start working nicely.

WSS: Technique for creating CAML field definitions.

Additional category: CAML

Here is a great posting by a fellow named "craig" on a technique for finding CAML definitions for field content types based off real live definitions from a site.

His posting says it all. In brief:

  1. Create a content type.
  2. Associate it with a list.
  3. Open up the list with SharePoint Designer.
  4. Export to a "personal web package".
  5. Rename to a .cab.
  6. Extract the manifest.
  7. Find your content type and associated CAML.

 

WSS: Summary steps to enable useful Features/Elements/etc. Intellisense in VS 2005

Additional categories: Visual Studio 2005

To enable useful intellisense for Features, elements, etc in visual studio 2005:

  1. Access a WSS server.
  2. Navigate to "c:\program files\common files\microsoft shared\web server extensions\12\TEMPLATE\XML"
  3. Open another windows explorer pointing to: "C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas"
  4. Copy the following files from the WSS server to your VS 2005 schemas directory:
    1. Wss.xsd
    2. CamlQuery.xsd
    3. CamlView.xsd
    4. CoreDefintions.xsd
  5. Restart VS 2005
  6. Add a new XML file to a project.
  7. Click in the body of the XML file.
  8. In the properties pane, click on "Schemas" and pick "wss.xsd" from the choices. (It’s not necessary to select other xsd’s since they are referenced from wss.xsd).

Intellisense is now enabled for that XML document.

See here for more information on this subject and for instructions on how to automatically associate WSS intellisense with any XML file.

WSS exam 70-541, Microsoft Windows SharePoint Services 3.0 – Application Development

I took and passed the above mentioned test this morning. I found the exam to be difficult and fair.

There is a relative dearth of information on this exam on the web. I’m not sure why.

I obviously won’t get into any detail about the exam I took, but I think I can safely say the following:

  1. Trust this link: http://www.microsoft.com/learning/exams/70-541.mspx.

    It lists what you need to know to pass the exam and it’s, IMO, very accurate.

  2. The link also recommends Inside Microsoft Windows SharePoint Services 3.0 by Ted Pattison & Daniel Larson. This book covers nearly all the subjects that the test addresses. I found it to be great in and of itself beyond simple exam prep. It provides great examples and really got me thinking about some new and interesting things to try. If you want to skip a few bits in the book, just cross-reference the book’s TOC with the items in the link from above.
  3. Nothing beats actual hands-on experience. If you have time and interest, follow along with the examples in the book and then experiment with them. You’ll have an easier time on the exam as well as really learn WSS application programming.

</end>Subscribe to my blog!

MOSS / InfoPath Forms Server (InfoPath 2007) drop-down list performance

Additional category: InfoPath

Summary: An InfoPath 2007 form deployed to a MOSS server provides a drop-down list of vendors tied to a custom MOSS list. Upon selecting a vendor, rules assign field values to a handful of text fields such as sales rep name, address, city, state, zip and phone. Performance is horrible. We notice that performance gets worse (in a non-linear fashion) for each additional field we update this way. I.e., if we just update the sales rep name, it takes [x] amount of time. If we update sales rep, address1, address2, city, state, zip, it takes 10 times longer.

Solution: Write a web service (sample code can be found here) that is passed in the name of a vendor and it returns back the vendor details. Then, assign the fields this way. Although this too seems slow, there was no discernable difference in performance when we assigned 1 field versus 8 fields. As an added bonus, users get a cool "contacting the server" Cylon effect while they wait for the form to invoke and consume the service results.

MOSS: Exception occurred. (Exception from HRESULT: 0x80020009 (DISP_E_EXCEPTION))

UPDATE: We never determined the root cause of this problem and it never surface again.

We notice during implementation of a development site that suddenly, two users are unable to access a site collection. Those accounts can authenticate to the main site, but when trying to access a particular site collection, they just get a blank screen. No errors displayed, just a white blank page.

We log in as a site collection admin and try to add one of those users as a site admin and this time, upon pressing "OK", we get this message:

Exception occurred. (Exception from HRESULT: 0x80020009 (DISP_E_EXCEPTION))

We spent some time researching this and unfortunately, didn’t come up with anything useful. There were some messages in the diagnostic log, but it was hard to exactly correlate them with this issue.

In the end, we deleting the site collection and re-created it and that solved it.

If I figure out what caused this in future, I’ll update this post.