Archives

Archives / 2004 / December
  • Creating or customizing Field Types

    Customizing SPS is sometimes a hassle. I'm not talking about customizing graphics or Frontpaging the pages into submission - I'm talking about extending the object model. Creating new templates for sites and lists, and so on. It's a hassle, which is why I'm always happy when things work out the way I expect them to. :)

    On today's menu - adding a new Custom Field Type to the Sharepoint list repertoire.
    This came out a bit longer than I expected, but I'm sure you'll pull through.

     

    Question One: Why?

     

    Sharepoint comes with a wide variety of field types. Text, of course, and others - URLs and Choice fields and Calculated columns and so on. Each has its own representation in the DB, and each has its own rendering pattern - how it is rendered into HTML when you view a View or a Form.
    Sometimes we want to create a new type of data to put in our fields. Let's say we have a document management system, and we access out documents using the following URL: http://docserver/getdocument.aspx?docid=xxx. Let's say we want to be able to add to an item in a Discussion list a new field containing a document link. It'd be a shame to make it a URL field and make the users always type out the first, static part of the URL when all they want to write is the document ID.
    Our example field will be a DocumentID field which automatically renders as a link to the document management system.

     

    Question Two: How?

     

    Step one: Creating the Field Type

    Field types are defined in the FLDTYPES.XML set in the C:\Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\lcid\XML directory. (1033 is the default English installation. 1037 is the Hebrew locale ID).

    This rather large (~300KB for a new installation) contains the definitions of all the field types, in a rather plain schema:

    <Row>

        <Field Name="TypeName" DisplayName="TypeName">Text</Field>

        <Field Name="InternalType" DisplayName="InternalType">Text</Field>

        <Field Name="SQLType" DisplayName="SQLType">nvarchar</Field>

        <Field Name="Sortable" DisplayName="Sortable">TRUE</Field>

        <Field Name="Filterable" DisplayName="Filterable">TRUE</Field>

    <RenderPattern Name="HeaderPattern" DisplayName="HeaderPattern" />

    <RenderPattern Name="DisplayPattern" DisplayName="DisplayPattern">

    <RenderPattern Name="EditPattern" DisplayName="EditPattern" />

    </Row>

     

    The Fields contain the basic information about the type:

    TypeName is the name we give our new type, to be used later.

    InternalType is the basic type that our type extends. Consider it a Base class.

    SQLType is the SQL datatype that is used to persist our field data to the SQL store.

    Sortable and Filterable – rather self explanatory.

     

    After these fields come several RenderPatterns (there are more which I cropped for brevity).

    These Patterns are rendering instructions – in CAML notation – that instruct SPS how to translate our field and the data within to HTML in each situation. The DisplayPattern, for instance, is how the data is rendered when the field is displayed normally – in a grid in the AllItems.aspx view, or as part of the Item Details screen in DispForm.aspx.

     

    For our DocumentID field, we will first change the TypeName of  the type:

    <Field Name="TypeName" DisplayName="TypeName">DocumentID</Field>

     

    To keep things simple, we will keep the current EditPattern as it is (a simple textbox) and change the DisplayPattern only.

    Originally, we have this:

     

    <RenderPattern Name="DisplayPattern" DisplayName="DisplayPattern">

      <Column HTMLEncode="TRUE" AutoHyperLink="TRUE" AutoNewLine="TRUE" />

      </RenderPattern>

     

    A simple pattern – the Column tag rendered directly.

    From the SDK: " The Column element is used to return the raw data value for a particular field". In our case – return the raw data saved in the SQL server. The text is automatically HTMLEncoded and its prefix checked for common protocol names (http://, ftp://, etc) and if found, automatically rendered as a hyperlink.

    What we'll want to do is remove the AutoHyperlink, and have the data automatically wrapped in a <a href> that is prefixed with the URL to the document management server:

     

    <RenderPattern Name="DisplayPattern" DisplayName="DisplayPattern">

      <HTML><![CDATA[<a href="http://docserver/getdoc.aspx?docid=]]></HTML>

      <Column HTMLEncode="TRUE" AutoHyperLink="FALSE" AutoNewLine="TRUE/>

       <HTML><![CDATA[">]]></HTML>

      <Column HTMLEncode="TRUE" AutoHyperLink="FALSE" AutoNewLine="TRUE/>

       <HTML><![CDATA[</a>]]></HTML>

      </RenderPattern>

     

     

    (I've tried to color-code the mess a bit. Dark red is for CAML tags. Blue is for the CDATA blocks, and black is the actual HTML that is rendered.

    What we have here is an HTML block which writes out <a href=", then the <Column> tag, then a closing bracket. This will form the following HTML:
    <a href="http://docserver/getdoc.aspx?docid=my column data">

    The second <Column> tag writes the data again, then we close the link. Final rendered output:

    <a href="linktodoc">DocumentID</a>

     

    Notes:

    • The CDATA blocks are there to allow us to write straight HTML without having to escape each and every < and >. They do add to the mess, though.
    • Remember that CAML's boolean values should always be capitalized. "TRUE", not "True".

     

    Step Two: Creating a derived field.

     

    Let's go to the standard SPS Topic area definition – SPSTOPIC, as defined in C:\Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\lcid\SPSTOPIC.

    Specifically, we will edit the SCHEMA.XML file for the Discussion board list template – under the LISTS\DISCUSS subdirectory.

     

    The SCHEMA.XML is also rather lengthy – it contains the definition for all the Views for this list. We will concentrate on only one area, though.

    Right at the top we have the List -> MetaData -> Fields node. It's initially empty (having only the basic fields inherited from the Base Type), but we will add a new one:

    <Field Name="DocLink" Type="DocumentID" DisplayName="Link To Document">

    </Field>

     

    This will add the DocLink field to all lists based on the DISCUSS list template. Now, when we add a new item to such a list, we'll see a "Link To Document" field – rendered as a simple textbox. We'll write our Document ID in this field, and save the item.

    When we open the item to view its properties, we'll see our DocumentID in all its glory – rendered as a link to our document management system.

     

    Don't forget that the new field will not automatically be added to the All Items view – we'll have to add it manually, either via the Manage List page or directly in the SCHEMA.XML file.

     

    Good luck.

    Read more...

  • Quick Tip: Erasing many Portal Areas.

    Erasing a Portal Area via the interface is a bit annoying - you go to the Manage Portal Site to get the Area Tree, right-click and Delete, then you have to confirm again, wait 10-15 seconds, then press another button to get back to the tree. If you're erasing a whole bunch of areas (on a development server, for instance, when you want to start a new batch of tests), it can take quite a bit of time and patience.

    Read more...

  • Creating or customizing Field Types

    Customizing SPS is sometimes a hassle. I'm not talking about customizing graphics or Frontpaging the pages into submission - I'm talking about extending the object model. Creating new templates for sites and lists, and so on. It's a hassle, which is why I'm always happy when things work out the way I expect them to. :)

    On today's menu - adding a new Custom Field Type to the Sharepoint list repertoire.
    This came out a bit longer than I expected, but I'm sure you'll pull through.

     

    Question One: Why?

     

    Sharepoint comes with a wide variety of field types. Text, of course, and others - URLs and Choice fields and Calculated columns and so on. Each has its own representation in the DB, and each has its own rendering pattern - how it is rendered into HTML when you view a View or a Form.
    Sometimes we want to create a new type of data to put in our fields. Let's say we have a document management system, and we access out documents using the following URL: http://docserver/getdocument.aspx?docid=xxx. Let's say we want to be able to add to an item in a Discussion list a new field containing a document link. It'd be a shame to make it a URL field and make the users always type out the first, static part of the URL when all they want to write is the document ID.
    Our example field will be a DocumentID field which automatically renders as a link to the document management system.

     

    Question Two: How?

     

    Step one: Creating the Field Type

    Field types are defined in the FLDTYPES.XML set in the C:\Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\lcid\XML directory. (1033 is the default English installation. 1037 is the Hebrew locale ID).

    This rather large (~300KB for a new installation) contains the definitions of all the field types, in a rather plain schema:

    <Row>

        <Field Name="TypeName" DisplayName="TypeName">Text</Field>

        <Field Name="InternalType" DisplayName="InternalType">Text</Field>

        <Field Name="SQLType" DisplayName="SQLType">nvarchar</Field>

        <Field Name="Sortable" DisplayName="Sortable">TRUE</Field>

        <Field Name="Filterable" DisplayName="Filterable">TRUE</Field>

    <RenderPattern Name="HeaderPattern" DisplayName="HeaderPattern" />

    <RenderPattern Name="DisplayPattern" DisplayName="DisplayPattern">

    <RenderPattern Name="EditPattern" DisplayName="EditPattern" />

    </Row>

     

    The Fields contain the basic information about the type:

    TypeName is the name we give our new type, to be used later.

    InternalType is the basic type that our type extends. Consider it a Base class.

    SQLType is the SQL datatype that is used to persist our field data to the SQL store.

    Sortable and Filterable – rather self explanatory.

     

    After these fields come several RenderPatterns (there are more which I cropped for brevity).

    These Patterns are rendering instructions – in CAML notation – that instruct SPS how to translate our field and the data within to HTML in each situation. The DisplayPattern, for instance, is how the data is rendered when the field is displayed normally – in a grid in the AllItems.aspx view, or as part of the Item Details screen in DispForm.aspx.

     

    For our DocumentID field, we will first change the TypeName of  the type:

    <Field Name="TypeName" DisplayName="TypeName">DocumentID</Field>

     

    To keep things simple, we will keep the current EditPattern as it is (a simple textbox) and change the DisplayPattern only.

    Originally, we have this:

     

    <RenderPattern Name="DisplayPattern" DisplayName="DisplayPattern">

      <Column HTMLEncode="TRUE" AutoHyperLink="TRUE" AutoNewLine="TRUE" />

      </RenderPattern>

     

    A simple pattern – the Column tag rendered directly.

    From the SDK: " The Column element is used to return the raw data value for a particular field". In our case – return the raw data saved in the SQL server. The text is automatically HTMLEncoded and its prefix checked for common protocol names (http://, ftp://, etc) and if found, automatically rendered as a hyperlink.

    What we'll want to do is remove the AutoHyperlink, and have the data automatically wrapped in a <a href> that is prefixed with the URL to the document management server:

     

    <RenderPattern Name="DisplayPattern" DisplayName="DisplayPattern">

      <HTML><![CDATA[<a href="http://docserver/getdoc.aspx?docid=]]></HTML>

      <Column HTMLEncode="TRUE" AutoHyperLink="FALSE" AutoNewLine="TRUE/>

       <HTML><![CDATA[">]]></HTML>

      <Column HTMLEncode="TRUE" AutoHyperLink="FALSE" AutoNewLine="TRUE/>

       <HTML><![CDATA[</a>]]></HTML>

      </RenderPattern>

     

     

    (I've tried to color-code the mess a bit. Dark red is for CAML tags. Blue is for the CDATA blocks, and black is the actual HTML that is rendered.

    What we have here is an HTML block which writes out <a href=", then the <Column> tag, then a closing bracket. This will form the following HTML:
    <a href="http://docserver/getdoc.aspx?docid=my column data">

    The second <Column> tag writes the data again, then we close the link. Final rendered output:

    <a href="linktodoc">DocumentID</a>

     

    Notes:

    • The CDATA blocks are there to allow us to write straight HTML without having to escape each and every < and >. They do add to the mess, though.
    • Remember that CAML's boolean values should always be capitalized. "TRUE", not "True".

     

    Step Two: Creating a derived field.

     

    Let's go to the standard SPS Topic area definition – SPSTOPIC, as defined in C:\Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\lcid\SPSTOPIC.

    Specifically, we will edit the SCHEMA.XML file for the Discussion board list template – under the LISTS\DISCUSS subdirectory.

     

    The SCHEMA.XML is also rather lengthy – it contains the definition for all the Views for this list. We will concentrate on only one area, though.

    Right at the top we have the List -> MetaData -> Fields node. It's initially empty (having only the basic fields inherited from the Base Type), but we will add a new one:

    <Field Name="DocLink" Type="DocumentID" DisplayName="Link To Document">

    </Field>

     

    This will add the DocLink field to all lists based on the DISCUSS list template. Now, when we add a new item to such a list, we'll see a "Link To Document" field – rendered as a simple textbox. We'll write our Document ID in this field, and save the item.

    When we open the item to view its properties, we'll see our DocumentID in all its glory – rendered as a link to our document management system.

     

    Don't forget that the new field will not automatically be added to the All Items view – we'll have to add it manually, either via the Manage List page or directly in the SCHEMA.XML file.

     

    Good luck.

    Read more...

  • SPS and Renaming AD Accounts

    We've been seeing some strange behaviour after we rename a group or user in the Active Directory:
     
    When we go to the Site Settings to define site-wide permissions, the old username is still displayed. If we remove it and then add it again, we see the new name in the Choose Group list - but after we add it, it's shown as the old group. Humorous, but harmless.
    The bigger problem is when you give such a group permissions on a specific area or list. If we try to remove the old name from the permissions list, we get an ID Not Found error . When we try to add the new group to the permissions list, we get a Group Not Found error.
     
    All this is caused by SPS storing user data in the UserInfo table in the DB - it caches the old usernames, and for some reason uses that as the key when trying to add/remove groups to Areas, rather than the SID (which is also saved for every entry).
     
    The solution we found for this was to manually change the UserInfo entry, or better yet - erase it and let SPS recreate it when we add the group again. Naturally, it's not something we want to be doing in a production DB. Another minus is that the ACLs for various areas and lists point to the UserInfo row, not directly to the AD. This is a good thing generally, but means that once we erase the old entry, it's erased from all ACLs - we have to go and give that group/user permissions again. If this is a working server, we probably don't want to do that, and we'll just change the tp_Login field - leaving room for typos and human error.
     
    Anyone know why SPS doesn't re-cache the Username from the AD when it changes? It DOES keep the SID for each entry, so it does have the AD entity's unique key - it should use this, and keep the Login name purely as a cache.
     

    Read more...

  • Web Part Versioning

    If you have a web-part on your development machine that occasionaly decides to die - give you Not Marked As Safe errors even though it is, and requires removal and readding for it to work (with no other steps necessary), you may have a version mismatch.

    Read more...