Wednesday, May 25, 2005

Quick Launch Bar in SharePoint areas

WSS sites and workspaces contains a quick launch bar that by default show all SharePoint lists and listings in a site, provided that they are flagged with 'Display this [list] on the Quick Launch bar' (QLB). This setting is available in 'Modify Settings and Columns' when managing the content of a SharePoint list. It is also available when creating new lists and listings. By default the WSS quick launch bar shows lists in these categories: Documents, Pictures, Lists, Discussions and Surveys.

What has puzzled me for some time is that the quick launch bar is never shown in SharePoint Portal Server 2003 areas, even if I flag e.g. a document library in the SPS area with 'Display ... on the QLB'. So I opened a SPS area with FrontPage 2003 to check if the area contains any code for showing the flagged lists, and the area did not. I had used FP2003 several times before on WSS team-sites to change the QLB, e.g. to remove the 'Surveys' groups header and to add links to other web part pages in the same team-site. So I decided to try to copy code from a WSS team-site to a SPS area. This works fine and is actually quite simple to do.

Start by copying the applicable code for the QLB groups that you want in your area from an existing WSS site, e.g. the 'Documents' group for showing all applicable document libraries:

<table border="0" cellpadding="0" cellspacing="0" class="ms-navframe">
<TR>
<td id="onetidWatermark" class="ms-navwatermark" dir="ltr"> </td>
<TD class="ms-navheader" width="126" style="padding-right: 2px;"><A HREF="_layouts/1033/viewlsts.aspx?BaseType=1">Documents</A></TD></TR>
<TR>
<td id="onetidWatermark" class="ms-navwatermark" dir="ltr"></td>
<TD style="height: 6px">
<!--webbot bot="Navigation" S-Btn-Nobr="FALSE" S-Type="sequence" S-Rendering="html" S-Orientation="Vertical" B-Include-Home="FALSE" B-Include-Up="FALSE" U-Page="sid:1004" S-Bar-Pfx="<table border=0 cellpadding=4 cellspacing=0>" S-Bar-Sfx="</table>" S-Btn-Nml="<tr><td><table border=0 cellpadding=0 cellspacing=0><tr><td><img src='_layouts/images/blank.gif' ID='100' alt='Icon' border=0>&amp;nbsp;</td><td valign=top><a ID=onetleftnavbar#LABEL_ID# href='#URL#'>#LABEL#</td></tr></table></td></tr>" S-Target TAG="BODY" --></TD></TR>
</table>


The code contains a small webbot that looks up the flagged listings and builds the links to those listings. In addition, I have added an extra <TD> element to get the same indent as the other content of the SPS area navigation panel.

Then you need to locate the slot in the SPS area navigation panel where you want the quick launch links to be added. A good location is below the 'Current location' and the 'Category navigation' SharePoint components. Locate this web control with FrontPage and add the copied code just below the CategoryNavigationWebPart component:

<SPSWC:CategoryNavigationWebPart runat="server" id="VerticalNavBar" DisplayStyle="VerticalOneLayer" />

<!-- ADD QUICK LAUNCH LINKS CODE HERE -->

<SPSWC:ToolBar runat="server" id="ActionBar" RenderIfEmpty="false" Orientation="Vertical" TitleLocId="CategoryManagement_ActionBar_Title_Text">

Save the SPS area after adding the code with FP2003, then do a hard refresh (ctrl-F5) of the page in MSIE to check that your changes are functioning as expected.

Note that you must change the location ID (1033) of the QLB code if your SharePoint installation is not using the US English version. Use e.g. 1044 on a Norwegian SharePoint version.

Monday, May 23, 2005

The Exchange RUS and external AD contacts

One thing that the Exchange 2003 SDK does not warn you about when adding mail enabled external Active Directory contacts is the Exchange Recipient Update Service (RUS), and this will cause strange effects when Exchange tries to resolve mail addresses. I.e. you will not be able to send e-mail to external mail contacts. Note that these effects will not happen immediately due to the periodical processing schedule of the RUS, thus you can test and believe that your code is correct, only to get hit in the face a few hours later.

The problem is that even if you set only a SMTP mail address on the new AD contact as shown in the SDK, the RUS will apply all the other types of mail addresses defined in your Exchange policies during its processing. This will give the external contact an internal Exhcange address, an X.400 address, etc. Thus, Exhcange will not be able to resolve or deliver e-mails to these contacts as most of these addresses will be bogus.

To correctly mail enable an external AD contact you need to set a GUID in two specific Exchange properties on the AD contact:

DirectoryEntry adContact = adContainter.Children.Find("CN=" + contactId, CONST.AD_CONTACT);

//exlude from RUS
adContact.Properties["msExchPoliciesExcluded"].Value = "{26491CFC-9E50-4857-861B-0CB8DF22B5D7}";
adContact.Properties["msExchPoliciesIncluded"].Value = "{26491CFC-9E50-4857-861B-0CB8DF22B5D7}";


//set mail AD property to e-mail address
//must set again after exluding from RUS
adContact.Properties["mail"].Value = mailAddress;


// Write Exchange information to the directory.
adContact.CommitChanges();


The policy GUID must be set on both the Exchange RUS policies excluded list and the policies included list. The specified GUID is valid for Exchange Server 2003. Refer to my previous post for details about how to write code for Exchange Server 2003.

You should also ensure that you never add an internal user's mail address as an AD contact through code. If your code does not prevent this, it will prevent Exchange from delivering incoming e-mail to the user's mailbox as it cannot resolve the mailbox when both an AD user and an AD contact has the same address.

Note that you should not exclude mail enabled AD distribution group (mailing lists) from the RUS, as this will cause the mailing lists to stop working. They must be processed by the RUS to function properly.

Saturday, May 14, 2005

SharePoint areas and topics for newbies

When you start implementing a SharePoint Portal Server 2003 (SPS) solution for the first time, you soon begin to wonder what is really the difference between areas and topics. SPS contains a top-level area called Topics out-of-the-box, which contains several areas and that allows you to add even more areas to Topics. So how are topics and areas related to each other ?

The answer lies in the art of Information Architecture (IA), which is one of the main strengths of SPS, in addition to the indexing and searching functionality provided by SPS. As your solution grows to tens of thousands of WSS team-sites and your SPS areas gets filled up with an abundance of content, and your users have a hard time of finding the information they need, then your SPS portal is in great need of IA (actually IA was needed a long time ago, before stuff got out of hands).

Information architecture typically has these aspects (L. Rosenfeld, P. Morville):
  • The combination of organization, labeling, and navigation schemes within an information system
  • The structural design of an information space to facilitate task completion and intuitive access to content
  • The art and science of structuring and classifying web sites and intranets to help people find and manage information
Information architecture (IA) is a very important part of implementing a SPS portal, i.e. determining how to customize the portal to fit the needs of an organization.

Thus, you need to device how to organize your SPS content and your site directories to help your users find their way around the portal without always resorting to searching. IA defines a set of typical ways to organize content known as the LATCH acronym:
  • Location (region, office, etc)
  • Alphabetical
  • Time-based
  • Classification (a.k.a categorization, taxonomy, and a whole lot of other terms)
  • Hierarchical (by issue, product, price, size, etc; any measure that allows for ranking content)
Organizing content in SharePoint is actually based on the principles of LATCH.

If you look at the metadata that SPS by default allows you to enter for new SPS areas and for WSS team-sites that you register in a SPS site directory, you will see that the metadata fits into LATCH. Then you should look at the different views provided by SPS for a site directory, and even make your own view that uses the metadata for e.g. grouping the team-sites. What you now have done is to provide your users with a new structured way of finding the WSS team-site they are looking for, much like finding books in a library by using a card catalog. The same way of organizing content applies to SPS areas and their content; metadata is used by SPS topics to give the users different views of the content stored within SPS.

In short, SPS have two main IA mechanisms for organizing content: areas and topics. Areas are the physical way of structuring and classifying information, while topics are logical views of the information (like SQL tables and SQL views). Thus, areas define the navigation structure of the portal, while topics organizes the content by structuring the information space of the portal. Use areas to make it easy for content contributors to add and maintain information assets in the portal, and use topics to make it easy for users to find and use the available information.

Another central aspect of IA, in addition to navigation and structuring content, is helping users find information. One of the most important aspects of SPS is the indexing and search scopes. SharePoint search should be configured according to your identified IA categories to enable and drive findability of information and documents stored in the portal and in all the WSS team-sites, in addition to selected external web-sites and information sources.

A final, commonplace advice on planning that holds true for all kinds of architecture and design: IA is important before adding stuff to SPS as it is most likely that a site design that starts with well-designed information architecture will be easier to implement and maintain and will be less likely to need an extreme makeover in the near future, leading to lower maintenance costs in the long run.

A good introduction to IA in general is found here: http://www.reallysi.com/newsletter13_1.htm

Wednesday, May 11, 2005

InfoPath: Add a row to a repeating table with JScript

One of the most typical questions on the InfoPath newsgroup is how to programmatically add a new row to a repeating table. The XML DOM is very fine-grained when it comes to adding new elements and attributes, demanding a lot of boring code especially when adding stuff like a new row to a table. Unfortunately, MSXML does not allow for adding an XML fragment from a string, which would have made things very simple. InfoPath script uses the MSXML component.

The trick is an old one with MSXML and bad horror movies: cloning !

This code shows how to create a new row, reset the values, and add it to the XML document:

//get parent row
var parent = XDocument.DOM.selectSingleNode("/dfs:myFields/dfs:dataFields//s1:InvoiceCommission");

//get first row
var rowOne = parent.selectSingleNode("./s1:InvoiceDetailsRow");

// Create xsi:nil attribute with the proper namespace.
var xmlNil = parent.ownerDocument.createNode(2, "xsi:nil", "http://www.w3.org/2001/XMLSchema-instance");
xmlNil.text = "true";

//clone the first row
var rowClone = rowOne.cloneNode(true);

//reset values
var description = rowClone.selectSingleNode("s1:Description");
description.text = "CLONED";
rowClone.selectSingleNode("s1:IsVatCharged").text = 0;
rowClone.selectSingleNode("s1:VatAmount").text = 0;

//nillable: The order is important. Attribute must be removed when setting actual value.
var amount = rowClone.selectSingleNode("s1:NetAmount");
amount.text = "";
amount.setAttributeNode(xmlNil);

//append row to XML document
parent.insertBefore(rowClone, rowOne);

Your XML must ofcourse contain a "seed" row in the table, otherwise there will be nothing to clone. Do not use parent.appendChild() as this can make your XML document invalid against your XSD schema. This will happen when the XSD defines a <xs:sequence> and the row element you add is not at the very end in the schema definition.

Note that MSXML does not have an .insertAfter() method. The .NET assembly System.Xml does, but this will require you to use managed code in your form, which makes deployment more complicated than a script based solution.

Monday, May 02, 2005

Do not use XSD default values with InfoPath

I have had some strange behavior in InfoPath with numeric fields that became locked even if they were not configured to be read-only. These fields were never locked when using the form to enter new data, but sometimes when modifying existing data.

After a bit of investigation, I found out that when an element in the XSD has e.g. default="0" and the user does not change it to something else, then this value will not be included in the XML when submitting to the web service. When the user later on opens the saved data, the element will not be in the XML returned by the web service. This will cause InfoPath to lock the field in the form, as InfoPath cannot bind to an element that is not there. This is very annoying, and setting default values in the InfoPath form will not make this go away. A field that contains a value different than the XSD default value, will be in the XML and thus fully editable.

In short, do not use the XSD default attribute in schemas intended for usage with InfoPath. It is sad that a client application imposes its weaknesses on the contract schema design, but the best is the enemy of the good, so I changed the schema to suite InfoPath.

Note that I have only tested this in combination with a WSCF web service.