Nov15

SharePoint Breakthrough: Inherited Content Types in CAML Content Type Definition

This week, I stumbled across a very interesting problem in SharePoint that could’ve easily put my entire design into jeopardy. Lucky for me, I can post now that I escaped this beast handily after only a few hours of absolute terror. For the benefit of anyone else who walks down this path, I’ll try to recount my experiences.

I have three different SharePoint projects going on right now. Among other concerns, I’ve recently been focused on our deployment strategy. It’s a good time for this, because I spent a few weeks mocking up a couple sites and developing some custom features. Now, we need to move those sites into our UAT environment from the virtual server on my laptop.

There are a few tools for doing this, namely the VSeWSS, Solution Generator, and WspBuilder. Until recently, I mostly used the WspBuilder and post build actions to deploy some of my simpler projects in Visual Studio. This strategy works pretty well for situations where you need to test an assembly over and over again. It was also the only option (until recently) if you had assets that needed to be copied outside of the FEATURES folder. On top of that, the 1.0 version of VSeWSS had some serious issues, so there wasn’t much good reason to switch strategies.

To deploy my mockup sites, I wanted to just basically create site and list definitions and move them over to the new server. I decided to give VSeWSS 1.1 CTP a try and see if I could use it to do this. While the CTP has some issues and challenges, I feel comfortable now saying that once you have worked with it for a while and understand how it behaves, it is overall a pretty good tool. The solution generator is ideal for replicating a site on the site server or packing up a site created entirely by a user. But, if you have a desire to leverage functionality like content types or list receivers, you’re going to end up to pushing its limits – often.

Today, I’m going to describe one of those limits, how it scared the living crap out of me for about two hours, and how you can rise to this challenge and overcome it.

If you are like most developers, you probably want to get traction in SharePoint as quickly as possible. This helps engage the customer and get the feedback process started so our designs are better. And, if you know SharePoint, you know it’s probably a pretty darn good idea to create Site Columns and Content Types instead of just slapping fields into a list; it’s the same amount of work, more reusable, and even required for certain functionality (like search) to work. It was with this idea in mind that I had created several lists with content types.

Of course, I want to re-use everything I created in the GUI, so I was now in the position of needing to create content type definitions based on what I’d already done during our discovery period. The Solution Generator that ships with VSeWSS does half this job for you, because content types are embedded with the list definition. They aren’t perfect of course, but I needed to clean up some field names anyhow. (IMHO, if you’re writing the definition file anyway, there’s just no good reason to have “_0x0020_” peppered throughout all your field names.)

So, I would use VSeWSS to create new (empty) content types, then paste the XML from the list definition, and massage it a bit to get the Fields and FieldRef elements in the right format. This isn’t a bad approach especially if you’re still learning your way around the wss.xsd schema, and it was a good opportunity to refine my approach and refresh my memory. (It had been over a year since my last official SharePoint gig, after all.)

In fact, this technique works fine, but there are a couple of things that aren’t entirely obvious at first glance that can trip you up when it comes to content types and feature definitions.

Firstly, if you’re content types are using the same GUIDs as the ones you created in SharePoint, then you are probably going to notice that your changes don’t always appear on the web site after you deploy. There are two reasons for this that I’ll explain later. The other thing you’ll run up against is that it doesn’t seem possible to use the ContentType element to define a type that inherits from another content type. There doesn’t appear to be any [documented] way to do it.

These two problems are actually related. To understand what is going on here requires a little background on how SharePoint manages Content Types.

SharePoint keeps track of Content Types in many places. They can have Site or Web scope, meaning that they are available in either the root site (top level web) or an individual sub web and its sub webs. Within each of these scopes, it is possible to derive a new type based on a parent type, even when that parent type is defined in a parent web site.

Actually, that’s a pretty nifty thing if you think about it, and as developers we want to be able to take advantage of that inheritance as often as possible to save us some work in both development and maintenance. Up until now, developers have tried to do this in their projects, but found that the only apparent ways to do so were either in the web UI or via the object model. This introduces another issue, in that once a content type is modified in the UI or API, it only gets its information from the content database SQL Server, and never from an XML element manifest. This would seem to rule out using features as a way to deploy new content types, unless of course you are writing a ton of code to create the content types programmatically, and we all know how much fun that can be!

Let me take a step back for a minute. Once you have content types defined in your site or web, you may have noticed that if you use them in a list, a mirror image of your content type will appear in each list. Actually, that’s kind of a cool feature too, because it may be that in one particular case you need to change the name of a field or some other property. Having the list based content type allows us to do that without affecting the parent and all the other lists that use our content types.

So, we have content types stored in the root site (both independent and inherited from other CTs), in the sub-webs (again, either independent or inherited), and in the lists themselves (always inherited). Let that last one linger in your mind for a bit, because that’s how I was able to figure this whole mess out.

I had noticed a while ago that when I use the Solution Generator to create a list definition, the content types always had IDs that were significantly longer that the ones generated if I created a new Content Type using VSeWSS 1.1 CTP. From the beginning, it had become pretty obvious to me that the latter of these was probably a GUID with some salt (“0x0100”) sprinkled in front of it. But, the GUIDs from the list definition were much longer – which was weird. I wondered a bit about this, and decided to use the GUIDs created by Visual Studio instead of the ones in the list definition, but I didn’t dig into it any deeper until today.

I’ve seen some folks have called these “magic GUIDs”, and some hacks have been documented where you can create a content type in the GUI and then steal these magic IDs to use in your Content Type manifest files, and the inheritance will follow them. Of course, any technology – sufficiently advanced or undocumented – will seem like magic. Obviously there isn’t some spirit that is following these funny ID attributes around and thus magically bestowing a parent-child relationship upon them. Or, is there? Could there be a Ghost in the Machine – literally?

Or, should that be "Re-ghost in the Machine"? It seems like all this talk of physical vs virtual content type definitions seems somehow eerily familliar. :-)

Anyway, I had an intuition that these mystery strings had something to do with the missing ability to define parent-child relationships, so I decided to do some digging and a few controlled experiments to see if I could unravel this inscrutable conundrum. Of course I’ve already spoiled this great detective story, because I already told you that I saved the day, disaster was averted, and I avoided being in a terrible mood for the rest of the week.

Firstly, these strange strings are made entirely of hex digits. This fact is made obvious by the leading “0x” at the start of the ID. Also, they are almost universally (but not always) some length that is close to a multiple of 32, which happens to be the length of a GUID with the dashes taken out. But, some are not. Well, if you keep in mind that 0x0100 and 0x000100 mean exactly the same thing, then things start to line up a little better. Allow me to demonstrate using the IDs of several ContentType elements that I generated in VS.net. I have inserted underscores to pad them out (not a legal substitution of course, but good for visualization purposes.)

So from this:

<ContentType Name="Item" ID="0x01006964b2a9ff5c4a299a55daf0ca370a79" Group="Development" Version="0"><FieldRefs /> </ContentType>

We get:

Item:          0x____0100 6964b2a9ff5c4a299a55daf0ca370a79

And so on…

Document:       0x__010100 28ec491792cd40469e5023279d495a74
Event:          0x__010200 0d11c8f44fee4d299fa7412976ced2f3
Issue:          0x__010300 d0ff4d76b975418f92f99b6380525b51
Announcement:   0x__010400 ca7fe46207ee4f678658688328478ac9
Link:           0x__010500 94746e4cec53476faf5c25b625375005
Contact:        0x__010600 d74d210576bb41d1ad7c2129f6b2f146
Task:           0x__010800 39f9376dd88d446fa540ae6b0aab80b2
Folder:         0x__012000 667c88b5ec704512acf75070fd6a9cb8
Discussion:     0x01200200 08993122a65146068f4acfc1f7be60f5
XMLDocument:    0x01010100 afce5e1e9dd84a7abc719987a7bd1627
Picture:        0x01010200 bc7bc4a9bb6c4dbe924eccebcf2e260a
WikiDocument:   0x01010800 8898737798e045a6bcbf8ae3617da0ac

So now, at least for the basic types, it seems pretty obvious that the first eight digits contain the base type that the content types is derives from.  This makes me ask myself all sorts of strange questions, like “What happened to 0x0107?” and “What other types that I don’t see here (Project Task List anyone?) could I exploit if I knew their codes?” But, my mission is clear, so moving on.

I also exported some Content Types from my own list definitions, and I observed some meaningful things about them. I will spare you having to stare at another list of random GUIDs, and simply explain what I found.

·         Once you line up the strings so that the first GUID always starts in the same positions, as above, the remainder of the string always follows a consistent format. Additional characters on the string consist of a “00” pad (0-FF being a single byte), followed by 32 hex characters (a GUID). This pattern will repeat for the remainder of the string.

·         I have seen strings with two or three GUIDs stored in this way. I assume that since the ContentTypeID field (in the content database in SQL) is a 512 byte binary block, that the developers anticipated needing to store up to 17 GUIDs in this way.

·         Assuming that your site is working, the “last” (low order) GUID of any ID attributes generated by exporting from it will be unique. No other content type will (or should) ever have that GUID.

·         The high order GUIDs will not be unique. Somewhere in SharePoint there will be another content type that has that GUID as a segment of its ID.

·         If you want to create an inherited content type as part of your feature, create its ID in the format “0x” + Base ID hex + Parent GUID + “00” Unique GUID. I have tried this and it works. If believe that if you want to create multiple inheritance, you do it like “0x” + Base ID hex + Grandparent GUID + “00” + Parent GUID + “00” Unique GUID, but I haven’t confirmed this. Also, don’t forget to take the dashes out.

·         List content types are simply inherited types that have the web/site content type as a parent. However, they do not appear to be stored in the ContentTypes table in SQL. Maybe they are embedded within the list itself?

So why would the SharePoint development team implement inheritance in this way and not as a property of the ContentType element (BaseType for example)? I don’t know the answer, but I suspect that it had something to do with a freeze on code near the end of development, or perhaps there were different teams relying on the schema and it couldn’t be easily changed. That much I can forgive, but this methodology was so unintuitive that it makes me laugh just thinking about it.

Here’s what Microsoft has to say about the ID attribute:

ID

Required Text. Specifies the content type ID of the content type.


While I can’t find anything factually wrong about that statement, I still have to say that it seems like something important is missing. ^_^

I found this article Content Type IDs after the fact, and indeed is it a very helpful elaboration on what I've explained above. Also, it seems to indicate that in addition to mulriple "00"+Guid combinations at the end of the first GUID, you can append any hex string. "You can simply append two hexadecimal digits to the myDocument content type ID." In fact they say this is a good way to stay below the 512 character limit. I wonder however, if it actually works and how inheritance is implemented in this case. (Since they don't do this themselves when you create content types OoTB with the SharePoint GUI, I have to question the accuracy on this point.)

So how are the two issues that started this conversation related? Well, it turns out that what the Solution Generator is extracting along with lists are the List Content Types. It looks like I will have to build a tool to export the actual Content Type from the web itself, after which the List Definitions should work without any issues, but all the related gripes about content type disconnection will still apply.

But at least for now, they'll be a bit less frustrating knowing that at least I can do what I want without writing let another pile of code to make it happen. Thank fate for small victories.

Related posts/articles:

Published: Nov-15-07 | 2  Comments | 0  Link to this post

Nov07

The War on Scarcity: It’s Not Just for Breakfast Anymore

So, in my previous post about [Great American Novelist] Cory Doctorow, I made an off-handed comment about the War on Scarcity. I turned to my cube mate, and asked him if he knew what the War on Scarcity was, which he did. (He's also a fan of Cory). So, I got to thinking. Where did I hear that term "War on Scarcity"? Why do some people seem to instinctively know what it means?

Well, Google says that the only references to the term "War on Scarcity" online are references to a 2004 speech by Tim Sanders (Steve's 2 Cents has a retelling of it here), and are self improvement related in nature. Basically, in this use it is a call for people to reject modern consumerist thinking. Appreciate what you have and not what you lack.

But, the War on Scarcity is really much larger than that, and it is only just beginning. It is not something that you can solve simply by becoming a Buddhist and rejecting your material desires. It is a drama that has been unfolding for thousands of years. It has been smoldering - not a full-fledged war - but there have been skirmishes. You see its echoes in history: the Magna Carta, Marxism, and DRM. It's part of what it means to be a human being. While just as there are pacifists in any war, individuals may turn away from materialism, the conflict will continue to unfold around them.

We are now at a critical nexus in this drama. There are signs that the opening salvos of the true war to come have already been fired, and the events that take place in the next 50 years will have a profound effect in that they will tell us the nature of the conflict that will likely continue for a thousand years into the future.

But, to understand a War on Scarcity, one needs to understand the nature of Scarcity itself. Scarcity has been with us since man first walked the earth. Historically, and perhaps ironically, we are accustomed to having an abundance of scarcity. We have always struggled to have enough resources to go around, whether it is food, land, raw materials, fuel, money, or energy. As man has progressed, we've continually found ways to produce resources in great abundance. So, we shift our attention to solving the problems of scarcity at a higher level. Instead of struggling for food, we start wars over oil. But, do we take the abundance that we create and truly eliminate the scarcity?

Think about it. As just an example, there is now enough food in the United States to feed a large part of the world - so much so that the government has an active interest in propping up the price of basic foodstuffs like corn. Corn itself is now so cheap that we find corn starch and corn syrup are added to practically every type of packaged food product we eat. Corn is now cheap, so the masses can now have it is quantities that are greater than are healthy. We slurp down corn laden Coca-cola with our Doritos, and get fat. Our bodies are not evolved to eat large amounts of empty corn calories every day. One might even say that the scarcity was better for us in some ways than the abundance.

Instead, we could feed the starving masses of the world (and, to our credit, sometimes we try), but by and large any attempt to do this is impeded by the systems we have in place around the world to prevent it. We call those systems governments. Ostensibly, we set them up to ensure justice and provide for the common good, but often they just act as a machine for perpetuating unnecessary scarcity. Whether the corn is rejected for being genetically engineered or is stolen by warlords really doesn't matter; either way the result is that someone goes without a meal.

Where the scarcity is resolved, new scarcities arise. Some people, who have more money than others, choose to buy steak (raised on corn) or whole and organic foods. Is there a scarcity of cows that not everyone can afford steak? Is there a scarcity of vegetables grown without pesticides? And what of the scarcity of paper money that causes some people have enough of it to overcome these scarcities while others can't? Some scarcities are real. There is only so much land after all. Some are manufactured. As long as someone is paying farmers to grow more corn than they really need (or can profitably sell), then there will be less for cattle and carrots.

Some are entirely imaginary.

And that is where things get very interesting. If you look at the course of human history, you can easily get the sense that at least some of us are very comfortable with scarcity. We now have systems put in place all around us that perpetuate and even celebrate scarcity. We have a money system based entirely on faith, investment markets composed entirely of rules and too complex to truly understand, and bank vaults full of notes that no human will redeem within our lifetime. We have erected a cadre of laws for intellectual property that makes it a crime to take something from another human being while at the same time also allowing them to keep it. Given that in the whole sphere of human knowledge each success rests upon the accomplishments of those who came before us, this idea is itself so tenuous that half the world considers it a fallacy. We endow corporations with the rights of living men, while consciously overlooking that they are immortal giants who nearly cannot help thinking of "ordinary" men as mere insects. We are categorized: employee, customer, consumer - competition.

Unlike the War on Poverty, the War on Drugs, and the War on Terror, the War on Scarcity was not declared by any government. Actually, calling it the War on Scarcity is perhaps a bit perverse, because it was really started by those who favor scarcity. But, like those other "wars", it does have an enemy which is partly – if not entirely – make-believe. However, this does not mean that the stakes are not high or that the consequences of the conflict can't be felt by you and me.

I can't sit on my soap box and pretend that I know all there is to know about the War. Perhaps I will be able to articulate that at some later point in time. For now, it was my hope that I can convey a general sense of the idea that is represented by the term. As a way of doing this, I would like you to consider a few thought exercises.

Suppose that tomorrow a very smart man develops in his garage a means for creating virtually limitless energy. If this is too hard, imagine that he merely finds a way to get much more use out of the energy sources we already have – say triple our efficiency. Suppose that man has a choice: he can choose to publish his method to the entire world, or he can keep his method secret and instead sell the fruits of his labor - namely the energy it produces or saves. If he does the latter, does he benefit more from selling more energy more cheaply, or does he benefit more from selling less of it at a higher price? Suppose he wants to take the first option instead. Do we live in a society that would celebrate this man as a hero, or like John Galt would we make him learn to regret his generosity?

There are now 3D printers that can create products out of plastic from plastic powder using only a CAD drawing. It is not so difficult to imagine a future, not unlike the one in Printcrime, where we have such devices that are capable of making virtually anything out of common raw materials like metal, paper, plastic, or wood. Suppose you had such a device in your home. Is there anything that you should not be allowed to make in it? What if you wanted to make a pistol? What if you wanted to make a grenade? How about a box full of grenades or a basement of weapons? Suppose you had a much larger version of such a device in your garage? Would you make a car with it? If you had or could easily download the blueprint for a '66 Camero, what would car companies have to offer you to convince you to pay $30,000 for one of their blueprints instead? What about the time period where not everyone has a garage sized printer? Would you sell a car to your neighbor or give it away? If you didn't have one, how would you feel if you knew that GM or Toyota were actively trying to keep you from getting one?

What does it mean for a copyright to be infinitely renewable? If a corporation can live forever, and the intellectual works of its employees are the intellectual property of no single man, but only of the organization, then does that mean that they remain copyrighted indefinitely? Is it right that Walt Disney was able to transfer the copyright for Mickey Mouse to a corporation that under the DMCA will continually renew it for all time to come? Should the Public Domain encompass the works of William Shakespeare but not Steamboat Willie? Didn't both have a profound impact to human history, and was Walt cheating death this way or only cheating himself?

The next time you download a song for free over the Internet, or hear people debating that topic after a story on the evening news, you should think about this. We are getting smarter – collectively. We are solving problems of technology at a rate that is increasing exponentially. But, we are adapting to our new abilities much more slowly, because the concept of not having enough is very deeply engrained into the human consciousness. We are, after all, living and mortal beings with physical needs. But just as it is now not entirely impossible to give practically every human being alive a complete box set of the works of Paul McCartney on MP3, it will someday be possible to give every human being a TV, and X-Box 360, a car, a house, and a diamond ring.

Some people will ask what will compel people to work in a world where they can have anything they desire at virtually no cost. Others will say "Who cares, because we'll have practically anything we want." The answer will either be wonder or fear. The more we try to prop up scarcity today, the more we are delaying and magnifying the inevitable conflict to come. And, I am certain that we will be very lucky indeed if it does not eventually come to people killing each other. That is the essence of the War on Scarcity.

Published: Nov-07-07 | 0  Comment | 0  Link to this post

Nov07

Overclocked: A Collection of Stories by Cory Doctorow

This blog was getting a bit SharePoint heavy. I can't have everyone thinking that all I do is code. :-)
 
Lately, I have been reading stories from Overclocked. Monday, I finished When Sysadmins Ruled the World, which left me with great difficulty getting to sleep. Let's just say, "Creepy!" and leave it at that. Anda's Game, which I finished last night was much more uplifting, but longer than I expected. This morning I was starting I, Robot. But, I have no opinion of it yet, as I only read a few pages into it.
 
I haven't read as much of Cory as some people have. I came onto the scene a little late. My wife introduced me to it; she's an avid SF reader and aspiring writer (I say "aspiring", because she is currently in recovery from a decade long addiction to writing fan fiction, and now has several very promising stories in the works). Also, I don't read very often or quickly on account of my bad eyesight. But, I know a good thing when I see it, and once I pick up one of Cory's books, I have a very challenging time putting it down again on the same day.
 
If you haven't read any of Cory Doctorow's work, you are in luck, because he makes everything available for free download under the Creative Commons. Now, I like the feeling of getting away from my PC with a great page turner, so I guess I'm doing my part to keep food on his table. Hats off to Cory for figuring out that you can give away your work and thrive as a result.
 
In fact, I'd like to go on record as saying that I think Cory Doctorow will become one of the classic writers of our time. By "our time" I mean the early 21st century. I believe that teachers - if they are not doing so now - will be making dispondent sixth graders read his books twenty years from now. (If you're a teacher dfoing this now, I'd love to hear from you.)
 
Why do I think that? Because I think that he writes using elements and themes that are both essentially important to people of our era, and yet also timeless. As we stand on the doorstep of the 21st century, we hear the earliest shots in the War on Scarcity whizzing over our heads, and we wonder what they are. When that war has been fought and lost - when the corporate giants are brought down by a mountain of ants - then maybe we'll look back on the works of Cory Doctorow as mere historical curiosity. More likely we will see them as the first notes of a rising chorus of thought, and in the intervening thousand years between then and now, I think his influence will be felt again and again.
 
The more I read, the more I continue to find Cory's insight into human nature as it relates to the conflict between property rights and individual freedom very enlightening. Perhaps some day soon I will try to put more of this philosophical thinking down in writing. For now, just go read some for yourself. At the very least, when your kids need help with it in middle school, you'll be able to lend a hand.
Published: Nov-07-07 | 0  Comment | 0  Link to this post

Nov06

CAML Intellisense in Visual Studio 2005

So, I was packaging up a custom solution today using VSeWSS 1.1, and I realized something important.
 
Apparently, I had configured Visual Studo to understand the schema for wss.xsd, but not any of the other files from the <12hive>\TEMPLATES\XML folder.
 
As it turns out, if you don't provide all the deployment files, then your element manifests and feature files will not have intellisense. So, I just expanded my catalog file a little bit.
 
To save you a lot of cut-and-paste, here's the wss_catalog.xml file. Just put it in your C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas folder and VS should pick up on it right away, though you might need to exit and re-enter Visual Studio.
 
Published: Nov-06-07 | 0  Comment | 0  Link to this post

Nov05

OMG WTF VSeWSS?! Object Reference Not Set

First, I'd like to personally thank The Definite Article, the WSS Wiki, and Sir Paul Liebrand for providing the background that helped me figure this incomprehensible error out in less than a month.
 
If you've used the VSeWSS to create a SharePoint solution and deploy it to your development server, then you probably know about the infamous "Object Reference Not Set" bug in it that has had many a hearty developer run back to the comforting arms of WspBuilder. Of course, this issue was not fixed in the CTP version of the VSeWSS v1.1 released in August.
 
I wait with bated breath for the day (real soon now?) when the final release of 1.1 comes out. However, I simply couldn't wait to get around this problem. Unfortunately,  Paul's EXE did not work for me, becaue it refused to connect to the instance of SQL Server that my content databases are stored on.
 
So, I wrote my own, independently. I'm glad to say that some neat innovations came out of it. Firstly, mine takes a URL instead of a connection string. You can get the connection string from SPSite.ContentDatabase.ConnectionString instead. Second, I learned that the reason for this error is generally always that an attempt to read SPFeature.Definition returns null if there is no feature.xml file in the FEATURES folder with the required matching GUID.
 
Sadly, I could not create a solution MS would suppport, because trying to call SPFeature.Remove() also causes the error. I suppose you could create a feature.xml file first, and then try this, but I have no idea if that would even work without resetting the application pool and re-retreiving the SPSIte/SPWeb objects or what-have-you.
 
Knowing this, it's hard for me to imagine that this is going to just stop being an issue when the new VSeWSS comes out. THere are just  too many other ways you can accidentally orphan a feature in SharePoint. I suspect we'll be hearing from this one for a long time.
 
So, without further adieu, here's the source code/ Enjoy!
 
Published: Nov-05-07 | 5  Comments | 0  Link to this post

Nov05

OMG W[o]TF CAML!? Choice Field Pet Peeve

Actually I had a different pet peeve today, but I'll blog that later. There'll be some code - and ice cream for those of you who get here while there's some left.
 
Okay, SharePoint can awesome, but sometimes it can also be pretty damned stupid. Here is a classic example of that dichotomy.
 
This is the documentation for a CHOICES / CHOICE element collection in CAML, from the MSDN version of the WSS 3.0 SDK.
 
CHOICES Element (List)

Used to define several choices within a field for a drop-down list.

...

CHOICE Element (List)

Used to define a choice within a Choice field.

<CHOICES>
  <CHOICE
    Value = "Text">
  </CHOICE>
  <CHOICE
    Value = "Text">
  </CHOICE>
  ...
</CHOICES>

Now, reading that, you might be led to believe that you could create a CHOICES collection like this to have SharePoint use value codes on the back-end of its DropDownList's options tag in HTML.

<CHOICES>
  <CHOICE Value="MD">Maryland</CHOICE>
  <CHOICE Value="DE">Delaware</CHOICE>
  <CHOICE Value="VA">Virginia</CHOICE>
  <CHOICE Value="PA">Pennsylvania</CHOICE>
</CHOICES>

Well, you would be WRONG! Wrong, as in "You get NOTHING! Good day sir!" wrong. In fact, as nearly as I can tell, the Value attrbiute of this element is less than useless. It's not invalid if you use it - sometimes. (Actually, sometimes it causes validation errors and sometimes it doesn't) It [usually] doesn't break anything; it just doesn't do anything, and the documentation leads you to believe that it should, which I think has the potential to cause a huge waste of time.

So, a big WoTF (wag of the finger) to Microsoft, for fragging this one up royally. And, I guess with that comment, I shouldn't expect to be getting that MVP nomination I've always wanted any time soon, huh? Oh well!

Update
I dug into this problem a little deeper. At first, I believed that you could provide a multi-column style value-text pair in the CHOICE element text, similar to what you'd find in a ModStat field type. However, this turned out to be a pipe dream.

  <CHOICE>MD;#Maryland</CHOICE>

As far as I can tell so far, what is actually going on is that the XSD schema for WSS is actually not consistent with the online documentation. See the following from wss.xsd:

  <xs:complexType name="CHOICEDEFINITIONS" mixed="true">
    <xs:sequence>
      <xs:element name="CHOICE" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
    </xs:sequence>
  </xs:complexType>

Well, it's no wonder when I try to use the Value attribute in my custom content types and other features, I get nasty XML validation messages from Visual Studio telling me that the Value attribute is no valid in the CHOICE element, because it's not.

I don't recommend you try this on a production box, but what I did was to patch wss.xsd as follows.

<!-- BEGIN CHANGE BY TCARPE 11/1/2007 -->
  <xs:complexType name="CHOICEDEFINITIONS" mixed="true">
    <xs:sequence>
      <xs:element name="CHOICE" type="CHOICEDEFINITION" minOccurs="0" maxOccurs="unbounded" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="CHOICEDEFINITION">
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="Value" type="xs:string" use="optional" />
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
<!-- END CHANGE BY TCARPE 11/1/2007 -->

That made Visual Studio and the VSeWSS behave themselves, but it's not clear to me yet if it will actually have any effect on the behavior of the Choice field and its DropDownList. In theory it should, since I see CAML in the RenderTemplate for CHOICE that retreives the Value attribute - if it exists.

From FLDTYPES.xml:

<HTML><![CDATA[<SCRIPT>fld = new ChoiceField(frm,]]></HTML> <ScriptQuote><Property Select="Name"/></ScriptQuote><HTML>,</HTML> <ScriptQuote><Property Select="DisplayName"/></ScriptQuote> <HTML>,</HTML> <ScriptQuote><Column/></ScriptQuote> <HTML>); fld.format = "</HTML>     <Property Select="Format"/>  <HTML>"; </HTML> <Switch> <Expr><Property Select="FillInChoice"/></Expr> <Case Value="TRUE">fld.fFillInChoice = true;</Case> </Switch> <ForEach Select="CHOICES/CHOICE"> <HTML>fld.AddChoice(</HTML> <ScriptQuote><Property Select="."/></ScriptQuote> <HTML>, </HTML> <ScriptQuote><Property Select="Value"/></ScriptQuote> <HTML>);</HTML> </ForEach> <Switch> <Expr><Property Select="Required"/></Expr> <Case Value="TRUE">fld.fRequired = true;</Case> </Switch> <HTML><![CDATA[fld.IMEMode="]]></HTML>      <Switch>       <Expr><Property Select="Type"/></Expr>       <Case Value="Lookup"><HTML><![CDATA[inactive]]></HTML></Case>       <Case Value="DateTime"><HTML><![CDATA[inactive]]></HTML></Case>       <Case Value="GridChoice"><HTML><![CDATA[inactive]]></HTML></Case>       <Case Value="Calculated"><HTML><![CDATA[inactive]]></HTML></Case>       <Case Value="Currency"><HTML><![CDATA[inactive]]></HTML></Case>       <Case Value="Number"><HTML><![CDATA[inactive]]></HTML></Case>       <Case Value="User"><HTML><![CDATA[inactive]]></HTML></Case>       <Case Value="Boolean"><HTML><![CDATA[inactive]]></HTML></Case>       <Default><Property Select="IMEMode" HTMLEncode="TRUE"/></Default>     </Switch>     <HTML><![CDATA[";]]></HTML> <HTML><![CDATA[fld.BuildUI();</SCRIPT>]]></HTML>

Clearly, somebody over there at MS was expecting this attribute to be declared - at least sometimes. I really have to wonder how this fell through the cracks, and if I'll be doing more harm than good with this particular tweak.

So, I just gave it a try to see what happens. Sadly, there is no joy in mudville today. Mighty SharePoint has struck out. Using this:

    <CHOICES>
  <CHOICE Value="C">Client</CHOICE>
  <CHOICE Value="P">Partner</CHOICE>
  <CHOICE Value="S">Staff / Professional</CHOICE>
  <CHOICE Value="V">Vendor</CHOICE>
  <CHOICE Value="O">Other</CHOICE>
    </CHOICES>

SharePoint gives me:

     <option selected="selected" value="Client">Client</option>
     <option value="Partner">Partner</option>
     <option value="Staff / Professional">Staff / Professional</option>
     <option value="Vendor">Vendor</option>
     <option value="Other">Other</option>

I give up! I don't think anything short of creating a custom field type is going to solve this problem.

The remifications of this "bug" are actually far reaching and profound. For example, let's suppose you have a custom field (or site column) with custom properties defined, and you want to assign a value from a CHOICES collection to a property of an enumerable type in your own class derived from SPFIeld. If the Value attribute were working as claimed, you could assign it the string equivalent of each value of the enumeration, then use a simple call to Enum.Parse() to do the conversion. Instead, you are left with the ugly choice of writing custom parsers. (Choose your posion: property attributes in the enum, or a hard coded switch-case statement.) And, you'll have to write one for each choice property you want to create in this way. This increases the overall amount of time spent doing code and maintainance, which is the opposite of why we use SharePoint in the first place.

For now, I guess the only thing I can really take away from this experience, other than frustration, is an affirmation of one of the great truisms of software engineering as coined by the irrerent Fred Brooks, "Documentation lies."

Published: Nov-05-07 | 0  Comment | 0  Link to this post

`