Tuesday, December 18, 2007

Guess the Artist

File this song under faith/Christianity in the mainstream. Can you guess the artist without google?

You, me
Understand
Shake my hand
Last chance, little man
Ain't it grand
It's a bargain, it's a steal
30 pieces of silver
And a deal's a deal
Sign upon the dotted line
I'll be yours and you'll be mine
Nothing's free
Eventually

Nothing's free
From the rules and laws of morality
Free to take your fill
Free from your own free will
Nothing's free

My boy, it's getting late
I'll raise the stakes
So close
Control your fate, why hesitate
Seal the deal, close the sale
Take my hammer, drive the nail
Sign upon the bloody line
A drop of yours, a drop of mine
Nothing's free
Eternally


Nothing's free
From your conscience or
Free from the consequence
Free to sin and death
Free till your final breath
Nothing's free
Free from the claws and flaws of your family
Free from obedient life
You're cut like a double-edged knife
Nothing's free, nothing's free


Oh, you pay me


Free to ignore the bore of authority
Free to spit in the face
Be the winningest rat in the race till
Judgement Day
Then nothing's free
Bow to me if you wanna be free
Free from life, come die with me
And when we're dead it's for eternity

Come on little one and dance in the fire
The heat's getting close and the flame's getting higher
When the music's over there's a hush in the choir
Nothing's free
When the trumpets sound and his light is all around
And the saints all raise from the graves in the ground
We'll be going way downtown
Way downtown


Update (12/24/2007)

Wow, thanks for that bevy of guess submissions. I'll never be able to weed through them all, so I'll just give you the answer here.

 LastTemptation

It's "Nothing's Free" from Alice Cooper's 1994 album The Last Temptation. Of interest (to me) is that this album had a comic book included with it which was written by one of my favorite fantasy authors, Neil Gaiman.

Lookup List DropDowns in SharePoint

For today's absurdity in SharePoint, I give you lookup list dropdown handling in the SharePoint UI.

Try this little exercise. Create three custom lists. In the third list create a lookup field connected to the title column of the first list. Create a second lookup field connected to the title column of the second list. Create 19 records in your first list, then create 20 records in your second list. Try putting "one, two, three, etc" in the title field for the records you are creating in these first two lists.

Now, create a record in the third list and notice what the dropdowns look like:

Notice how they are different? This is what it looks like when you click down on the first one vs. clicking down on the second one:

I'm guessing the thought process was that once the number of records in a list gets to be large (read: 20 or more) then the display of a lookup field based on that list is going to look unruly on the screen. It's actually not a bad looking dropdown or a bad idea, except it's a total PITA when trying to read/manipulate the contents of the control in JavaScript.

In a nutshell, the steps to programmatically select an item in the prettied-up dropdown through JavaScript is to emulate a click event on the down arrow image causing the list to appear, and then to emulate a double-click on the item you want. That's right, when you are selecting an item in this prettied-up dropdown through the UI, you have to double-click.

That's actually a simplified version of what you have to do. The in-between steps are so obnoxious between figuring out if the control has been changed, finding all the control pieces it's using and changing their states that I went about about finding a shortcut and present it to you here.

I've created a JavaScript wrapper object I call the SharePoint LookupDropDown. Here is the code:

var SP = function() {}
SP.LookupDropDown = function(cn) // cn = "cell name"
{
    var cll = ydom.getElementsBy(function(el) { return(el.id.indexOf(cn) > -1); }, "td")[0];
    var btn, fld, sel, IsFarged = false;
    this.Value;

    if (typeof(yelm) == "undefined")
        throw("Error in SP.LookupDropDown: \'yelm\' is missing.");
    if (typeof(cll) == "undefined")
        throw("Error in SP.LookupDropDown: invalid CellName specified."); else cll = new yelm(cll);
    if (cll.getElementsByTagName("input").length == 1) IsFarged = true;
    if (IsFarged)
    {
        btn = cll.getElementsByTagName("img")[0];
        fld = cll.getElementsByTagName("input")[0];
        this.Value = fld.value;
        try { btn.click(); } catch(x) { }; // trapping -- btn may be hidden
        sel = ydom.get(fld.opt);
        try { sel.style.display = "none"; } catch(x) { } // trapping -- control may be hidden
    }
    else
    {
        sel = cll.getElementsByTagName("select")[0];
        this.Value = sel.options[sel.selectedIndex].value;
    }
    this.GetSelectId = function()
    {
        return(sel.id);
    }
    this.GetValue = function()
    {
        return(this.Value);
    }
    this.SetValue = function(val)
    {
        this.Value = val;
        if (IsFarged)
        {
            try { ShowDropdown(fld.id); } catch(x) { }
                // ShowDropDown is a function baked into SharePoint;
                // will throw an error when the controls are hidden
            sel = ydom.get(fld.opt);
            for (var i = 0; i < sel.options.length; i++)
            {
               if (sel.options[i].value == val) sel.selectedIndex = i; continue;
            }
            try { OptLoseFocus(sel); } catch(x) { }
                // OptLoseFocus is a function baked into SharePoint;
                // will throw an error when the controls are hidden
        }
        else
        {
            for (var i = 0; i < sel.options.length; i++)
            {
               if (sel.options[i].value == val) { sel.options[i].selected = true; continue; }
            }
        }
    }
}

This function sits on top of a namespace I've created called "SP". You can also see that I have a couple objects called "ydom" and "yelm". These are friendly names to two components of the Yahoo User Interface Library (which I use a ton and highly recommend), YAHOO.util.Dom and YAHOO.util.Element. I've left them here because it saves me a ton of time from coding their equivalent functions, but if you want to code around it manually or substitute your own equivalent functions from your favorite library be my guest.

To use this script include it on your page where you have one of these prettied-up dropdowns. In SharePoint Designer (or your favorite html editor) find the cell/<td> element holding the lookup dropdown and give it an ID, such as "LookupCell". Make sure you haven't added any other controls into this cell, either. In your own JavaScript code pass in this ID when you instantiate an instance of this object:

var MyLookupDropDown = new SP.LookupDropDown("LookupCell");

I elected to instantiate the wrapper this way (by using the ID of the enclosing cell/<td> element) because the ID of the actual dropdown is somewhat random and can change. So now you'll be able to get/set the value of the dropdown by simply calling the GetValue or SetValue methods:

MyLookupDropDown.SetValue("blahblahblah");
var TheValue = MyLookupDropDown.GetValue();

There is another method in there called GetSelectId which I don't use anymore, but you may find it helpful. It returns the SharePoint-assigned ID of the core select control for the prettied-up dropdown.

Where might you use this? Well, I'll tell you where I've used it. I tend to assign lookup fields to the ID field of the source list, which means a lookup dropdown doesn't look too hot on a page unless you're some sort of savant who knows which item they want to select based on their ID number alone.

So, for a custom edit form I'll add the lookup field to the page as I normally would, but then hide the row containing it by adding a "style='display:none'" attribute in the <tr> element. I then assign an ID to the enclosing cell/<td> element holding this lookup field and instantiate the wrapper object using this cell ID. I call this the "source" dropdown.

Then I'll add an old fashioned select element to the page and populate it either through some onerous JavaScript or binding it to a data source. I call this the "surrogate" dropdown. I then attach an "onchange" attribute to the surrogate, which calls the SetValue method of the source. Now, when I save the record the correct information is being saved and the dropdown lists looks the way I want it to look.

A couple notes here:

  1. This dropdown list tweaking only occurs in IE.
  2. Yes, this wrapper object does detect whether or not the dropdown has been tweaked by SharePoint and sets the value of the"source" object accordingly.

Monday, December 17, 2007

Emerging: Theological Crap

I don't know if you've been clued in or have been paying attention to the pseudo-evangelical Christian movement known as "Emerging" or "Emergent," but it's becoming a popular venue for the younger generation on Sunday mornings.

In a nutshell, the Emerging movement basically features a "seeker friendly" gospel which eschews absolute truth save for one absolute truth, that you can not possibly know what is absolutely true. They favor an experience-oriented gathering, making the individual  the focus of the gathering rather than where the focus should belong - on the worship of Christ1. In my mind it's the marriage of post-modernity with Christianity, featuring generation-x folks who are now old enough to preach from the pulpit lead discussion from the center bar stool.

This video clip is from a recent PBS story on the movement, and apparently there will be a follow-up with one going a little more in-depth with one of it's leaders, Brian McLaren. I'll be on the lookout for it.

 

(1) Does that not have overtones from the original lie told in the garden?

Wednesday, December 12, 2007

Deploying Linked Lists in MOSS 2007

This post contains a process for re-linking lists you've already deployed to SharePoint, which to your chagrin may have un-linked themselves from their previous state in your development environment. You can skip the pre-ramble and read the detailed version I've brewed up or the terse version.

One of the insanely stupid things about developing in SharePoint is how little thought there was given to the process of creating and maintaining discreet development, testing, and deployment environments. For myself, I like to have a development environment where I am currently changing the code (I use a VM for this these days), and test environment where I can push what I think is good code and let the customer hack away at it, and then the production environment where the code is in a "live" state.

For developing regular web sites in .Net this is fairly trivial. Just copy the files from place to place. SharePoint is a completely different beast. For one, most (if not all) of the files you will be working on are handled by http handlers built into the SharePoint namespace (no code-behind for you!). Second, those files are not tangible entities you can browse around for on the server hard disk. They are "hosted" inside the content database for the site you are working on.

That's the biggest WTF you run into when learning to develop in SharePoint. What I really want to grump on today is the process of deploying custom linked lists or document libraries from a dev/test environment to a production environment.

When you think of a list in SharePoint, you think of something akin to a table in a database with columns of different variable types. That's good from an understanding perspective.

All list data for a site in SharePoint is stored in one physical table in SQL Server, though, with an insane number of columns with generic names (nvarchar1, nvarchar2, ..., nvarchar64 and repeat a few more times with a couple other variable types) to store the list data. Further, in order to figure out which of those generic column names correspond to the field names you created through the SharePoint UI, you have to find the list definition in another table and pull the contents of one of the columns out into a text editor. The contents are in XML format. What an abomination.

Where this abomination starts ruining your day is when you have a handful of lists that are linked to each other via a lookup field you've created through the SharePoint environment. When you get ready to push these lists to another server you typically save the lists as templates (with the content included), download the templates to your local machine, upload the templates to the production machine, and then create new lists based on those templates.

Because of the way SharePoint stores the list data it depends on a system of identifying lists with GUIDs, and when you create a new list in SharePoint based on a template you do not get the same GUID identified with the new list as you had on your dev/test environment. What that ultimately means is that any lookup fields you had on a list are hosed -- they point to nowhere.

Todd Bleeker, who has multiple black belts in SharePoint development and administration and who is well known in the SharePoint community, has a process for dealing with this. It involves cracking open the template prior to uploading to the production server and editing the one of the files contained within. It works, but involves uploading each list one at a time, finding out the generated GUID for that list, cracking open the next template, editing the file inside, reassembling the template, uploading, etc.

I've come up with an alternate way of re-linking your lists together after you've uploaded all of them to your production server. It involves opening SQL Studio or Query Analyzer and editing the column holding the field definition for your just-created lists. This alternate way also assumes that you are deploying your new list/library set to the same site. If not, you'll have to find another way yourself.

A caveat here -- everybody in the SharePoint community will tell you not to ever edit any of the data in a SharePoint content database outside of the SharePoint UI. Actually, I agree with that sentiment but it was 3 in the morning when I came up with this scheme and I liked it better than the Bleeker method. So, do this at your own risk and only if you are comfortable with using Microsoft's SQL Server tools and writing SQL. If you hose your content database, I don't know you but I do hope you have good backups.

So, create your templates, upload them to your production server, and create your list/document libraries from them. Then, crack open SQL Studio or Query Analyzer, open a query window to your content database and follow along.

The Detailed, Getting-Paid-By-The-Hour-To-Type-This Version

  1. You need to find your custom list and the field definitions for it in the content database. To do this run this piece of SQL:

    select
        Webs.Id WebId,
        Webs.SiteId,
        Webs.FullUrl SiteUrl,
        Lists.tp_ID ListId,
        Lists.tp_Title ListName,
        Lists.tp_Fields FieldDefs
    from
        Webs
        left join AllLists Lists on Lists.tp_WebId = Webs.Id
    where
        Webs.FullUrl = '[ your relative site url ]' and
        ((Lists.tp_Title = '[ list name 1 ]' and Lists.tp_DeleteTransactionId = 0x) or
         (Lists.tp_Title = '[ list name 2 ]' and Lists.tp_DeleteTransactionId = 0x) or
         (Lists.tp_Title = '[ list name 3 ]' and Lists.tp_DeleteTransactionId = 0x))

    What this SQL does is find the exact lists/libraries you've just created in the content database. You need some pieces of information here -- the relative url to the site where your list is located, and the name you've given to each of your new lists/libraries. These are put into the where clause.

    The first line of the where clause references the relative url to the site where your lists have been deployed. Your relative URL should look something like "sites/TheSite" if you've create a site collection at /sites, or it could look something like "thesite" if your site collection is at the root level of the site.

    The second, third and fourth lines of the where clause finds your lists/libraries based on the names you gave them. I've included three lines here, but you'll want to include a line for each list/library, whether it's referencing another list/library or is being referenced itself. You'll notice here that the where clause on each of these lines filters using the field "tp_DeleteTransactionId." This is because if you create a list/library in the past with the same name, SharePoint doesn't actually delete it's row from the database (at least, not right away). If you have created a list with the same name in the past but deleted it, this additional filter makes sure you are grabbing the list that has not been deleted.

    This query should return exactly one row for each list/library you've specified. If not, abort and try again.
  2. Next we're going to put the field definition info into something we can easily manipulate. For this you are going to need your favorite text editor. In your query results, find the list which needs to have it's external references fixed, copy the contents of the "FieldDefs" column to the clipboard, and paste it into your text editor. It would not be a bad idea to save a copy of what you pasted into your text editor as a backup somewhere should you need to restore the column content to its original state.

  3. Now we're going to find the lookup fields which need adjusting. What you'll get in your text editor is something that starts off resembling this:

    12.0.0.4518.0.0<FieldRef Name="ContentTypeId"/>
    <FieldRef Name="_ModerationComments" ColName="ntext1"/>
    <FieldRef Name="FileLeafRef"/>
    <FieldRef Name="Modified_x0020_By" ColName="nvarchar1"/>
    <FieldRef Name="Created_x0020_By" ColName="nvarchar2"/>
    <FieldRef Name="File_x0020_Type" ColName="nvarchar3"/>

    [ ... so on and so forth ... ]

    I'm sorry, but you'll have to ignore that first bit of creative Microsoft xml mangling at the very beginning (I think it's a version number they stick in there). What you're looking for in this xml are the definitions for your lookup field(s). You can do this in your text editor by doing a search for the word "lookup." You should find an xml node that looks something like this:

    <Field
       Type="Lookup"
       DisplayName="[ display name ]"
       Required="FALSE"
       List="[ a guid string ]"
       ShowField="ID"
       UnlimitedLengthInDocumentLibrary="FALSE"
       ID="{9f437a3e-5507-41e9-ab68-36beb3bd0822}"
       SourceID="{f3296739-dd99-4fd5-b756-020c359d9fbb}"
       StaticName="[ static name ]"
       Name="[ field name ]"
       ColName="int2" RowOrdinal="0" Group="" Version="4"
       WebId="[ a guid string ]"
    />

    I've replaced some attribute values of this node with placeholders, but you'll see that some of the attributes contain the name you've given to the lookup field. Of particular interest here are two attributes containing GUID values -- "List" and "WebId." These help SharePoint define the list being referenced to by this lookup field.
  4. Next we'll replace the GUID values in the xml with the proper ones from the database Go back to the query you ran and find the list that this lookup value is referencing. I'm assuming you know which list this should be, and that it was included in the where clause of the SQL statement you ran. When you find the list in the query results, copy the contents of the "ListId" column and paste it over the contents of the "List" attribute value in the xml node you are currently editing.

    Once you've done that, repeat this process with the WebId attribute by copying the contents of the WebId column in the query results and pasting it over the contents of the WebId attribute value in the xml node1.

  5. Repeat steps 3 and 4 for the other lookup fields in your list, if there are any more.

  6. Now we're going to update the field definition for this list in the content database. Go back to your query editor and stamp out this piece of SQL in a new window pointing at the same content database:

    update Lists set tp_Fields = '[ FieldDefs ]' where Lists.tp_Id = '[ ListId ]'

    Replace the "[ ListId ]" placeholder with the contents of the ListId column (a GUID value) of the list item you are editing in the original query.

    Go to your text editor and copy the entire xml text to the clipboard, then come back to your query editor and replace the "[ FieldDefs ]" placeholder with the contents of the clipboard. It's going to look real ugly, but don't fret it. Just make sure the contents of the clipboard are between the apostrophes.

    Run this SQL, and you should get 1 row update.

  7. You can now go in through the SharePoint UI and examine the lookup field definition for the list. The "Get Information From:" should read with the correct list name, whereas before it was blank.

  8. Repeat steps 2 - 7 for your other lists containing lookup fields.

This seems pretty complicated, but I was intentionally long-winded so that you'll understand what's going on with each step. The less verbose version of this process is here in the event that you don't want to weed through the explanations.

The Terse, Would-You-Please-Get-To-The-Point-Already Version

  1. Open Query Analyzer or SQL Studio and run this piece of SQL:

    select
        Webs.Id WebId,
        Webs.SiteId,
        Webs.FullUrl SiteUrl,
        Lists.tp_ID ListId,
        Lists.tp_Title ListName,
        Lists.tp_Fields FieldDefs
    from
        Webs
        left join AllLists Lists on Lists.tp_WebId = Webs.Id
    where
        Webs.FullUrl = '[ your relative site url ]' and
        ((Lists.tp_Title = '[ list name 1 ]' and Lists.tp_DeleteTransactionId = 0x) or
         (Lists.tp_Title = '[ list name 2 ]' and Lists.tp_DeleteTransactionId = 0x) or
         (Lists.tp_Title = '[ list name 3 ]' and Lists.tp_DeleteTransactionId = 0x))

    Make sure to a) replace the placeholders as appropriate and b) add/subtract lines from the where clause as appropriate for each created list which is referencing another list or is being referenced.
  2. Find the first list in the query results which contains lookup fields and copy the contents of the FieldDefs field to the clipboard and paste into a text editor.

  3. Find the first lookup field which needs to be changed. Do a search on the word "Lookup" and you should find a node which looks like this:

    <Field
       Type="Lookup"
       DisplayName="[ display name ]"
       Required="FALSE"
       List="[ a guid string ]"
       ShowField="ID"
       UnlimitedLengthInDocumentLibrary="FALSE"
       ID="{9f437a3e-5507-41e9-ab68-36beb3bd0822}"
       SourceID="{f3296739-dd99-4fd5-b756-020c359d9fbb}"
       StaticName="[ static name ]"
       Name="[ field name ]"
       ColName="int2" RowOrdinal="0" Group="" Version="4"
       WebId="[ a guid string ]"
    />

  4. Go back to the query and find the list that is being referenced in this xml node. Copy the contents of the ListId column (should be a GUID value) and paste it over the existing value in the "List" attribute.

    Repeat this process using the "WebId" column value from the query and pasting it over the "WebId" attribute value in the xml node.

  5. Repeat steps 3 and 4 for the other lookup values contained in the xml.

  6. Go back to your query editor and open a new window to the same content database:

    update Lists set tp_Fields = '[ FieldDefs ]' where Lists.tp_Id = '[ ListId ]'

    Replace the "[ ListId ]" placeholder with the contents of the ListId column (a GUID value) of the list item you are editing in the original query. Replace the "[ FieldDefs ]" placeholder with the xml from your text editor. Run the SQL and you should get 1 row updated.

  7. Examine the just-editing field through the SharePoint UI. The "Get Information From" should now read with the correct list reference rather than being blank.

  8. Repeat steps 2 - 7 for the other lists containing lookup fields.


Updates, Post-Scripts and What-Not

  1. I'm finding that there are times when the WebId attribute is missing in the XML. Skipping this seems to still glue the lookup field properly as long as you paste the correct ListId in.

Separated At Birth

  

On the left, Ryan Leaf, disgraced Chargers quarterback and the biggest draft bust of all time. On the right, Denise from the current season (I think it's #8?) of Survivor.

Monday, December 10, 2007

The Nutty Buddy

For those special occasions where you find your national treasures under attack from a hundred miles per hour baseball.

Feeling Ill at the Head Table

OK, I'm warning you in advance -- don't read on if you get queasy reading about how your food is processed prior to arriving at the supermarket, or if you're a vegetarian, or have a religious affiliation that is pork-adverse. Etc, etc etc.

According to a new article on Wired's web site, the "head table" in question is the station at a pork processing plant where the brains of a pig are extracted by blowing compressed air through the skull and squishing the "meat" out the other side. Apparently some of the workers are contracting an illness known as chronic inflammatory demyelinating polyneuropathy, or CIDP.

The theory is that the workers may be acquiring this illness through inhalation of pork brain mist occurring in the air through the de-braining process. See, I warned you and you read anyway.

The article is chock full of details:

In a rapid-fire process that is noisy, smelly and bloody, severed pigs' heads are cut up at the head table at a rate of more than 1,100 an hour. Workers slice off the cheek and snout meat, then insert a nozzle in the head and blast air inside until the light pink mush that is the brain tissue squirts out from the base of the skull.

[...]

Compressed air could turn some brain matter into a mist that could be inhaled by workers, said Mike Doyle, a microbiologist who heads the University of Georgia's Center for Food Safety. Or the workers may have come into contact with something dangerous and then touched their noses or mouths, he said.

Delicious. In the words of Tom T. Hall,

That's all there is to this small [ story ],
But waitress, before you leave...
Could you please bring me some coffee
And a hot ham sandwich, please?

Your Gas Dollars At Work

The Burj Dubai, on it's way to being twice the height of The Empire State Building and the tallest man-made structure on the planet.

Sunday, December 09, 2007

Tuesday, December 04, 2007

Is God a Human Invention?

If you have a couple hours to kill you can download and watch a debate between philosopher and atheist Daniel Dennett (author of Breaking the Spell: Religion as a Natural Phenomenon) and Dinesh D'Souza (conservative writer and one-time policy advisor to Ronald Reagan).

The download page is hosted at Richard Dawkins' site -- you can grab it as a QuickTime file or follows links to the video chunked out in segments on YouTube.

Saturday, December 01, 2007

Trivia Photo of the Day

Here's where Lori and I were a week ago today. Of course, there's a big clue in the picture already, so you're going to have to be a little more specific than the name of the establishment.

Here's another clue for you, though: it is where I first started financially contributing in support of the great system known as the U.S. Government.

Scott is not allowed to participate here, as he's going to know it right away.