A jQuery Library for SharePoint Web Services (WSS 3.0 and MOSS): Real World Example – Part 3

Cross-posted from EndUserSharePoint.com

Part 1 Part 2 Part 3 Part 4

Please forgive me; it’s been over one month since my last confession article in this series. When last we spoke, I left you with the details of how one of the Data View Web Parts (DVWPs) – sections A and B below – on the dashboard worked by using an AggregateDataSource, values passed in on the Query String, and complex links to other functionality. In this article, I’ll talk about the DVWP in section C.

Section C is really the “meat” of the page. This DVWP uses 8 (yes, that’s eight, ocho, huit) different lists for its DataSources. While this may seem excessive, it allowed us to have lists that we treated purely as relational tables from a content management perspective. While we didn’t get too granular with the permissions on these lists, by having them separated the way we did, we had that option going forward.

I won’t go into all of the details of what was stored in each list, but they follow all relational rules. If you look through the view above, hopefully you can see how things might break out. (Post a comment if you are interested in these details: maybe another article!)

There are a couple nice things going on in this DVWP which are probably worth calling out, though:

  • The little plus and its partner minus signs, and
  • The “button”

The Plus and Minus Signs

If you look closely at the first image above, you’ll see that there’s a little image to the right of the Project Charter Artifact Type. Rather than showing all of the artifacts that fulfill a specific requirement, we decided to hide them all, allowing the user to “open” the list sections if they chose to. This makes the DVWP a dashboard (when not showing the details) *and* a locus for activities the user might need to undertake (when showing the details).

I find that a dashboard that is just a dashboard gets far less visibility than one that has the business process embedded in it. This way, the people doing the work see the current state of things on a regular basis as well as those who might be monitoring things. This has been one of my tenets for effective knowledge management for years: embed everything you can in the business process.

In the image below, you can see what the DVWP looks like after clicking on the image. There are links for each artifact (in this view, just one artifact) to Open or View in the Actions column.

This is really a pretty simple thing to do, but may be beyond the capability of many DVWP builders because it isn’t possible without diving into the XSL. Here’s how I made it happen:

First, if there are artifacts in place for the requirement, I output the image. The IMG gets a unique ID formed by concatenating the string “PAimg_” (“PA” is short for Project Artifact) and the current item’s position in the rowset. I also set the onmouseover event to change the cursor to a hand and the onclick event to call the showArtifacts() JavaScript function (see below).

<img alt="" style="vertical-align:middle" id="PAimg_{position()}" onmouseover="this.style.cursor='hand';" onclick="showArtifacts(this);" src="_layouts/images/plus.gif"/>

Next, when I output the TABLE containing the artifact details, I wrap it in a DIV. The DIV has a unique ID formed by concatenating the string “PARows_” and the current item’s position in the rowset. I also set the display:none so that, by default, the DIV won’t be visible.

<div id="PArows_{position()}" style="display:none">
  <table cellpadding="0" cellspacing="0">
    <tr valign="top">
      <th nowrap="">Actions</th>
      <th nowrap="">Type</th>
      <th nowrap="">Filename</th>
      <th nowrap="">Version</th>
    </tr>
    <xsl:for-each select="$ArtifactList">
      <xsl:call-template/>
    </xsl:for-each>
  </table>
</div>

Finally, the JavaScript. Those of you who have been following along with these articles may ask “Why not jQuery?” Well, this is such a simple little function that there really isn’t any need to incur the overhead for jQuery. If I already had jQuery loaded in the page for something more complex, I would certainly use jQuery selectors instead. This is a good example of a case where jQuery is *not* necessarily a good idea, as I discussed in several posts over on my blog.

The net-net of this function is that it simply toggles the display of the DIV and the and signs.

<script language="javascript">
  function showArtifacts(obj) {
    thisId = obj.id.substr(obj.id.indexOf("_")+1);
    thisPA = document.getElementById("PArows_"+thisId);
    if(obj.src.indexOf("plus.gif") > 0) {
      obj.src = "_layouts/images/minus.gif";
      thisPA.style.display = "block";
    } else {
      obj.src = "_layouts/images/plus.gif";
      thisPA.style.display = "none";
  }
}
</script>

The Upload “Button”

It’s not really a button, but just a little image I built to add a little graphic-ness (graphic-icity?) to the page. We wanted to make it clear that this was where you clicked to add artifacts. (Yes, a few people were confused by the word Upload.) Clicking on the image passes the context for the row you’re clicking on as well as some parameters on the Query String that makes things work smoothly.

<img alt="Upload" src="/sites/CSO/KR/PublishingImages/Upload.png"/><a href="/sites/CSO/KR/_layouts/Upload.aspx?List=%7B41BBE5C5%2D5321%2D4D07%2D8EFC%2D10B064F85E6E%7D&amp;RootFolder=%2Fsites%2FCSO%2FKR%2FSDLC%20Artifact%20Repository%202010%2F{translate($RequestID, ':', '-')}&amp;MultipleUpload=1&amp;Source=http://{$SERVER_NAME}{ddwrt:UrlDirName(string($PATH_INFO))}/SDLC%20Artifact%20Repository%202010/Forms/EditFormBulk.aspx?RootFolder=%2Fsites%2FCSO%2FKR%2FSDLC%20Artifact%20Repository%202010%2F{translate($RequestID, ':', '-')}%26ProjectInfo={$ProjectID}|{$RequestID}|{@Artifact_x0020_Name}|{$URL}">Upload</a>

That looks pretty complicated, so let me break it down a little. We have to do a little fancy footwork to pass the values we need to make this work.

First, the image itself. Simple enough:

<img alt="Upload" src="/sites/CSO/KR/PublishingImages/Upload.png"/>

Next, the link. We decided to *always* send the user to the multiple file upload page. In many cases they will be uploading multiple artifacts anyway, so why have them deal with two different processes? (This never really makes sense to me in the basic SharePoint functionality, anyway.) The way you can make this happen is to have the link go to Upload.aspx with the MultipleUpload=1 Query String parameter. We also need to pass the GUID for the list with the List Query String parameter, and the RootFolder. Here, we’re putting the artifacts for each project into a unique folder which is named based on the project’s RequestID (with the colons replaced with dashes because folder names can’t contain colons). I’ve added carriage returns below to make this [hopefully] more readable:

href="/sites/CSO/KR/_layouts/Upload.aspx
?List=%7B41BBE5C5%2D5321%2D4D07%2D8EFC%2D10B064F85E6E%7D
&amp;RootFolder=%2Fsites%2FCSO%2FKR%2FSDLC%20Artifact%20Repository%202010%2F{translate($RequestID, ':', '-')}
&amp;MultipleUpload=1

Next, I needed to provide the context I’d need on the customized EditFormBulk.aspx page. By default, when you upload multiple documents, you’re just sent back from whence you came with the metadata for each document unset. Not a good thing, IMHO, as it requires the user to remember to go back in to set everything. This is definitely a place where things fall through the cracks.

So here I’m going to pass EditFormBulk.aspx (my customized metadata-setting page) as the “Source”, along with some values I need on that page to make things work well. First, the Source page itself (not really the Source, but that’s where the user will be redirected from Upload.aspx), then the RootFolder where the documents will have ended up, then a Query String parameter I called ProjectInfo. ProjectInfo is actually a compound value built up from the ProjectID, the RequestID, the ArtifactName, and the URL with the vertical bar (|) as a separator. Because I’m layering things in pretty deeply, I needed to combine these values and parse them out on the EditFormBulk.aspx page.

&amp;Source=http://{$SERVER_NAME}{ddwrt:UrlDirName(string($PATH_INFO))}/SDLC%20Artifact%20Repository%202010/Forms/EditFormBulk.aspx?RootFolder=%2Fsites%2FCSO%2FKR%2FSDLC%20Artifact%20Repository%202010%2F{translate($RequestID, ':', '-')}%26ProjectInfo={$ProjectID}|{$RequestID}|{@Artifact_x0020_Name}|{$URL}">Upload</a>

All of this ends up looking something like this:

/sites/CSO/KR/_layouts/Upload.aspx?List=%7B41BBE5C5%2D5321%2D4D07%2D8EFC%2D10B064F85E6E%7D&RootFolder=%2Fsites%2FCSO%2FKR%2FSDLC%20Artifact%20Repository%202010%2FDUMMYRequestID&MultipleUpload=1&Source=http://collaborate/sites/CSO/KR/SDLC%20Artifact%20Repository%202010/Forms/EditFormBulk.aspx?RootFolder=%2Fsites%2FCSO%2FKR%2FSDLC%20Artifact%20Repository%202010%2FDUMMYRequestID%26ProjectInfo=1234567890|DUMMYRequestID|Project%20Charter|/sites/CSO/KR/ViewRequest.aspx

What’s Next?

I had to do quite a bunch of complicated stuff to make this page look and work well, but it really provides a great springboard for user activities as well as an easy dashboard view of the project itself. The page does a lot, but looks pretty simple. It takes a few seconds to load, but no more, even given all of the complexity in the DVWPs.

In my next article, I’ll show what the multiple document metadata entry page (EditFormBulk.aspx) looks like and how it works. Teaser: Yes, it uses jQuery and my jQuery Library for SharePoint Web Services to get its jobs done quite extensively, specifically the Lists Web Service’s operations GetListItems, UpdateListItems, and CheckInFile.

My Blog’s First Year at WordPress and "Are You Being Served?"

On the occasion of my blogs’ first birthday here at WordPress (it’s longer in the tooth than that, but I migrated over from Windows Live Spaces on 2009-01-06), I wanted to have a party, but that seemed excessive.  Instead, in an a propos coincidence, I saw a post over at my friend Christophe’s fantastic Path to SharePoint blog entitled How to get your SharePoint question answered.

Path2SharePoint Path2SharePoint 5:18am, Jan 06 from Web — New post: how to get your #SharePoint [end user] question answered http://tinyurl.com/SPquestion

It seemed like a great idea for me to outline my own mental framework for answering all of the questions, suggestions, and raves (well, OK, not so many of those).  My thinking is pretty similar to Christophe’s, I think.

Issues or questions  on my blog posts

If you see something in my blog that you don’t think is right, then by all means let me know with a comment on the post.  I find enough things out there on the Interwebs that I think is sub-optimal or just plain wrong and none of that does us all any service.  I welcome error corrections, constructive criticisms, etc., but flaming insults don’t usually make the moderating screen.  I let almost every legitimate comment through, but only if it is civil.

Questions or requests for clarification are always welcome.  I post this stuff for general benefit, so if you need more information, especially if you think it will help others, then post it.  Also, sometimes I may have posted an update or a newer post on the same topic with more up-to-date information or a better approach. I try to link the posts if I do, but try a little searching with the search box on the right and you might find more to go on.

Issues with the jQuery Library for SharePoint Web Services

If you have questions or issues with the jQuery Library for SharePoint Web Services, I would prefer that you use the Discussions or Issue Tracker on the Codeplex site.  This is mainly to keep things isolated over there that might help other users of the library.  Start with the Discussions and if we determine that something is a real issue, I can copy it over to the Issue Tracker.  Of course, if the context is a blog post about the library, then feel free to comment here on my blog.

General SharePoint questions

I try to answer every question I get, just like Christophe.  As any of you who have noticed my posts on the MSDN SharePoint – Design and Customization forum, my EndUserSharePoint.com articles, my posts on the EndUserSharePoint.com Stump the Panel forum (where I’m now the moderator of the jQuery Library for SharePoint Web Services Solutions forum), my Twitter activity, etc. may have found, I’m pretty dogged about it.  I firmly adhere to the adage “AN EDUCATED CONSUMER IS OUR BEST CUSTOMER”, made famous by Syms here in the States.  The more the SharePoint community knows about how to use the platform wisely, the better off the entire community will be.  No, I’m not entirely altruistic about this: I want to earn my chunk of the pie as well.  But the more everyone knows, the more we all gain.

I like the way Christophe puts this:

I’ll respond immediately if I can, but most of the time it requires some investigation, and I put your message on hold until I have a final answer (which sometimes means never…).

To be honest, there are only so many minutes in the day! In most cases you’re getting free advice, suggestions, etc., so it all happens based on whatever else is going on in my life whether it be work or personal.  Don’t forget that we all take vacations, have bad days, etc.  I can’t get back to you in 15 minutes EVERY SINGLE TIME, like some people seem to expect!

Finally, I work for a living, just like you [probably] do.  I am always looking for interesting projects for Sympraxis Consulting LLC to take on.  Sometimes the smallest little conversation on my blog turns into real paid work, but certainly not often.  If you like the way I approach things and have something I may be able to help with, let’s talk about it.

Happy birthday, blog!

Revived Stump the Panel: SharePoint Q&A at EndUserSharePoint.com

There’s a new, revived Stump the Panel over at EndUserSharePoint.com as of today.  Those of you who follow EndUserSharePoint (@EUSP) know about the difficulties they had last month when their hosting provider shut them down due to high trafiic.  (Sort of a good *and* bad problem to have!)

Well, Stump the Panel is back with a better interface, and I’m please to report that I will be moderating one of the forums about SharePoint and jQuery: jQuery Library for SharePoint Web Services Solutions.  This is a forum where we can discuss jQuery with SharePoint on any level that might interest you, as well as the specifics of my jQuery Library for SharePoint Web Services.  Yes, there’s the Discussions forum on the Codeplex site, but Mark and I figured that we could serve another or larger audience by implementing a forum at EndUserSharePoint as well. So yes, I’ll be trying to spread myself more thinly across the MSDN SharePoint – Design and Customization forum, SharePointOverflow, the Codeplex site, and EndUserSharePoint, but the important thing is to give folks options so that we can help them make the most of SharePoint.   The more the merrier!

Stop by and say hello, ask a question, leave an example of what you are doing with jQuery, etc.  I look forward to hearing from you!

A jQuery Library for SharePoint Web Services (WSS 3.0 and MOSS): The SPSetMultiSelectSizes Function

Cross-posted from EndUserSharePoint.com

SPSetMultiSelectSizes is a function in the jQuery Library for SharePoint Web Services that lets you set the sizes of multi-select picker boxes based on the values they contain. This may sound trivial, but because of the way SharePoint constructs the pickers as compound controls, it’s less straightforward than you might think.

Background

In an old post on my blog, I sketched out how you could set go about Setting Multi-Select Widths in a SharePoint EditForm.aspx Using JavaScript.  The code I posted worked just fine, but took some manual tweaking every time you wanted to use it.  The basic idea is this: When SharePoint renders the multi-select control, the <div>s which contain the selects have a fixed width of 143px.  (Who knows where *that* number came from?!?!)  This is all well and good in the example below.  All of the states fit well inside the <div>s and are quite legible.


But what about the case where the values in your multi-select column are much longer? In the example below, I’m showing a multi-select column which takes its values from the list of Web Services operations which the jQuery Library for SharePoint Web Services currently supports. (Yes, I keep track of things in SharePoint lists.  Isn’t that wonderfully recursive?)  As you can see, it would be very easy to choose the wrong value from among those which start with “GetGroupCollection” unless you scroll to the right, which feels cumbersome at best.


So what I wanted to do was to build a function into the library to deal with this better, and now I have SPSetMultiSelectSizes in v0.4.8ALPHA1. Sure, it’s an alpha, but it works; give it a whirl.

This function led me on several wild goose chases. At first, I couldn’t for the life of me think of how I could reliably figure out what the right width should be. I wrote a post on my blog (Setting Multi-Select Picker Widths in a SharePoint Form Reliably) basically saying “Help!”. To make the function work regardless of the fontSize, fontFamily, fontWeight, etc., that a site may be using, I knew that I couldn’t take any shortcuts. After going back and forth with the ideas I had in that post, plus several others, it finally occurred to me that I could take advantage of the jQuery clone() function for this. What I really needed to do was to clone the existing select, tweak a few of the clone’s CSS attributes, and then use it to determine the best width for the multi-select picker boxes.

Here’s what I’ve come up with for v0.4.8ALPHA1. I expect that it will evolve a bit as it gets some real use, but the fundamental logic is sound.

$.fn.SPServices.SPSetMultiSelectSizes = function (options) {
      var opt = $.extend({}, {
            multiSelectColumn: ""
      }, options);
      // Create a temporary clone of the select to use to determine the appropriate width settings.
      // We'll append it to the end of the enclosing span.
      var possibleValues = $().find("select:[Title='" + opt.multiSelectColumn + " possible values']");
      var selectedValues = possibleValues.closest("span").find("select:[Title*=' selected values']");
      var cloneId = "SPSetMultiSelectSizes_" + encodeColumn(opt.multiSelectColumn);
      possibleValues.clone().appendTo(possibleValues.closest("span")).css({
            "width": "auto",        // We want the clone to resize its width based n the contents
            "height": 0,                  // Just to keep the page clean while we are using the clone
            "visibility": "hidden"  // And let's keep it hidden
      }).attr({
            id: cloneId,                  // We don't want the clone to have the same id as its source
            length: 0                     // And let's start with no options
      });
      var cloneObj = $("#" + cloneId);
      // Add all the values to the cloned select.  First the left (possible values) select...
      possibleValues.find("option").each(function() {
            cloneObj.append("<option value='" + $(this).html() + "'>" + $(this).html() + "</option>");
      });
      // ...then the right (selected values) select (in case some values have already been selected)
      selectedValues.find("option").each(function() {
            cloneObj.append("<option value='" + $(this).html() + "'>" + $(this).html() + "</option>");
      });
      // We'll add 5px for a little padding on the right.
      var divWidth = $("#" + cloneId).width() + 5;
      // Subtract 17 from divWidth to allow for the scrollbar     for the select   
      var selectWidth = divWidth - 17;
      // Set the new widths
      possibleValues.css("width", selectWidth + "px").parent().css("width", divWidth + "px");
      selectedValues.css("width", selectWidth + "px").parent().css("width", divWidth + "px");

      // Remove the select's clone, since we're done with it
      $("#" + cloneId).remove();
};

If you don’t want to understand the details of the jQuery above, here it is in pseudocode.

  • Based on the column’s DisplayName on the form, find the two selects in the boxes in the multi-select picker
  • Clone the select in the left box (“possible values”) and append it to the <span> which encloses the compound control
  • Change the CSS on the cloned select so that its width automatically adjusts based on its contents (this is what we’re used to selects doing most often)
  • Load all of the possible values into the clone. We need to grab the values from both the left (“possible values”) select and the right (“selected values”) select. By taking this approach, the logic works whenever it might be applied.
  • See what the width of the clone is and make a couple of small adjustments for the scroll bar and adding a little right side padding
  • Set the widths of the two boxes (actually a select wrapped in a div for each)
  • Remove the clone from the DOM, since we’re done with it

So, what does all of this work that I went through actually get you? Well, it may not seem like much, but it’s another arrow in your quiver to use in the battle against GIGO.

Example

Going back to the example above, we’d like to have the multi-select boxes in the “Web Service Operations” multi-select picker set just wide enough so that we can see all of the values without scrolling.

Here’s what the picker looks like “out of the box”…

…and after making the call to SPSetMultiSelectSizes:

Syntax

As with all of the functions in the jQuery Library for SharePoint Web Services, everything is put in place from the UI or SharePoint Designer. There’s nothing to install server-side. You simply need to add references to the core jQuery library and the jQuery Library for SharePoint Web Services and then call the function. See the documentation on the Codeplex site for more details on how to set things up.

SPSetMultiSelectSizes currently takes just one option:

 where multiSelectColumn should indicate the column’s DisplayName on the form.

$().SPServices.SPSetMultiSelectSizes({
    multiSelectColumn: "Web Service Operation"
});

 

I expect I’ll be adding a few more options along the way. A few things that come to mind:

  • Allowing you to specify an absolute width rather than having the function do the calculations for you
  • Specifying whether you want to allow the picker boxes to shrink in size, as they will currently if you have only short values

Conclusion

While this function doesn’t make any calls to the SharePoint Web Services, it seemed like a natural addition to the library. My initial inclination was to automatically call it from $().SPServices.SPCascadeDropdowns, but I’m not sure that makes sense, as it’s pretty simple to call it separately. As always, I’m interested in what you think and what enhancements you’d like to see.

A jQuery Library for SharePoint Web Services (WSS 3.0 and MOSS): Real World Example – Part 2

Cross-posted from EndUserSharePoint.com

Part 1 Part 2 Part 3 Part 4

In my last article, I showed how I used my jQuery Library for SharePoint Web Services to improve data quality by enhancing an out of the box form using the SPRequireUnique, SPDisplayRelatedInfo, and PreSaveAction functions. In this installment, I’ll show you how I created part of the nice dashboard-like page to display the current state of things to each Project Manager using Data View Web Parts (DVWPs). In this article, I won’t be showing any usage of the jQuery Library for SharePoint Web Services, but in a later article, I’ll show you how I use it again to enable easy bulk upload of the project artifacts.

Figure 1: Project Manager’s Dashboard

You’ll need to bear with me a little bit on this one. Because this example uses an actual client site, I’ve needed to doctor up the screenshots quite a bit. Hopefully you’ll still be able to get the general idea. In Figure 1: Project Manager’s Dashboard, you can see the dashboard for the Project Manager. This dashboard has three main components. In this article, I’m going to talk about sections A and B.

The top DVWP shows basic information about the Request (remember that a Request is like a sub-Project). In this first DVWP, I’m just displaying some of the important metadata about the Project and the Request. Since the data comes from items in those two lists, I’m using an AggregateDataSource. If you want to build your applications by using lists like relational tables (ALWAYS a good idea), you’ll need to become familiar with the AggregateDataSource concept.

This code is going to look a little messy, but here’s how you specify an AggregateDataSource:

<DataSources>
    <SharePoint:AggregateDataSource runat="server"  IsSynchronous="" SeparateRoot="true" RootName=""  RowsName="">
   <Sources>
   <SharePoint:SPDataSource  runat="server" DataSourceMode="List" SelectCommand="&amp;lt;View&amp;gt;&amp;lt;/View&amp;gt;"  UseInternalName="True">
   <SelectParameters>
   <WebPartPages:DataFormParameter  ParameterKey="ListName" PropertyName="ParameterValues"  DefaultValue="SDLC Projects"  Name="ListName"></WebPartPages:DataFormParameter>
   </SelectParameters>
   </SharePoint:SPDataSource>
    <SharePoint:SPDataSource runat="server"  DataSourceMode="List"  SelectCommand="&amp;lt;View&amp;gt;&amp;lt;/View&amp;gt;"  UseInternalName="True">
    <SelectParameters>
   <WebPartPages:DataFormParameter  ParameterKey="ListName" PropertyName="ParameterValues"  DefaultValue="SDLC Requests"  Name="ListName"></WebPartPages:DataFormParameter>
    </SelectParameters>
    </SharePoint:SPDataSource>
   </Sources>
   <Aggregate>
   <concat name="data source">
   <datasource name="SDLC_Projects" id="0" Type="SPList"/>
   <datasource name="SDLC_Requests" id="1" Type="SPList"/>
   </concat>
   </Aggregate>
   </SharePoint:AggregateDataSource>
  </DataSources>

When you create a Linked Source in SharePoint Designer (SPD), this is basically the code that you get. IMHO, SPD does a horrible job formatting the code it generates, so one of the first things I usually do is to clean up the formatting of what it creates. Unfortunately, any reformatting that you do of the DataSources will not “stick”; each time you save the page, SPD will mess it all up on you again.

The important sections of the AggregateDataSource are:

  • < SharePoint:AggregateDataSource> – This simply says that you are specifying a DataSource which includes multiple sources.
  • < SharePoint:SPDataSource> – You specify an SPDataSource for each individual source. In this case, the two lists: SDLC Projects and SDLC Requests. For simplicity here, I’m grabbing all of the items from each list (SelectCommand=”&lt;View&gt;&lt;/View&gt;”) but the SelectCommand is where you would specify any CAML you need.
  • <SelectParameters> – For each source, you need to indicate where it is. You do this with <WebPartPages:DataFormParameter>s. Note that I have switched from ListID to ListName. This can be helpful if you want to refer to your lists by their text name rather than their GUIDs. This also makes your code portable, in that SharePoint will just look for a list with the name you’ve specified rather than a specific GUID, which will be different across sites, Site Collections, or Web Applications.
  • <Aggregate> – In this section, you assign working names to the individual DataSources which you then use to refer to them later in your XSL. I typically just use the list name with any spaces replaced with underscores (there can’t be spaces here).

To create the links in section B, which is actually part of the same DVWP as section A, I needed some IIS Server Variable values. Another important “set up” section of the DVWP is the <ParameterBindings> section. Here it is with a few of the garden variety bindings left out.

<ParameterBindings>
  ...
   <ParameterBinding  Name="URL" Location="ServerVariable(URL)"  DefaultValue=""/>
   <ParameterBinding  Name="SERVER_NAME" Location="ServerVariable(SERVER_NAME)"  DefaultValue=""/>
   <ParameterBinding  Name="PATH_INFO" Location="ServerVariable(PATH_INFO)"  DefaultValue=""/>
   <ParameterBinding  Name="ProjectID" Location="QueryString(ProjectID)" DefaultValue=""/>
   <ParameterBinding  Name="RequestID" Location="QueryString(RequestID)"  DefaultValue=""/>
  </ParameterBindings>

Again, this is basically what the code looks like when you use the dialogs to create parameters. (At this point, I just go into Split View and write most of this myself because it’s faster for me.) There are three Server Variables I’m using: URL, SERVER_NAME, and PATH_INFO. You can find full documentation for the available Server Variables on MSDN. I’m also grabbing the values for the ProjectID and RequestID from the Query String. This is how the page “knows” what Project and Request we’re interested in.

OK, so I’ve got the DataSource set up, I’ve got the ParameterBindings set up, now it’s time for the XSL. Here are a few of the key bits.

<Xsl><xsl:stylesheet version="1.0"  exclude-result-prefixes="xsl msxsl ddwrt"  xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime"  xmlns:asp="http://schemas.microsoft.com/ASPNET/20"  xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls"  xmlns:ddwrt2="urn:frontpage:internal"><br>
   <xsl:output  method="html" indent="no"/>
   <xsl:param  name="URL" />
   <xsl:param  name="SERVER_NAME" />
   <xsl:param  name="PATH_INFO" />
   <xsl:param  name="ProjectID" />
   <xsl:param  name="RequestID" />

In this top part, I’m just declaring the stylesheet and specifying the xsl:params I built up in the ParameterBindings section. This makes their values available in the XSL as well.

Next comes the initiation template. I don’t actually know what this is really called, but I think of it as kicking off the whole process. It calls my first real working template, SDLC_Requests. SPD by default uses template names like dvt_1, dvt_1.body, etc., but I replace those names with names that match what list or data I’m working with to make it easier to follow.

<xsl:template match="/"  xmlns:asp="http://schemas.microsoft.com/ASPNET/20"  xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer"  xmlns:SharePoint="Microsoft.SharePoint.WebControls">
   <xsl:call-template name="SDLC_Requests"/>
  </xsl:template>

In the SDLC_Requests template, I simply grab the item from the SDLC_Requests list I want, and start building the table I want to display. (Think of xsl:templates as being sort of like subroutines.) I create a variable called Rows and select all of the items in the SDLC_Requests list which have a Title (I’ve repurposed the Title column for the RequestID) which matches the values on the Query String. If you remember, in the last article I made sure that the RequestID is unique per item. Next I call the SDLC_Requests.rowview template to generate the table rows.

<xsl:template name="SDLC_Requests">
   <xsl:variable  name="Rows"  select="/dsQueryResponse/SDLC_Requests/Rows/Row[@Title =  $RequestID]"/>
   <table  border="0" width="100%" cellpadding="2"  cellspacing="0">
   <xsl:for-each  select="$Rows">
   <xsl:call-template  name="SDLC_Requests.rowview" />
   </xsl:for-each>
   </table>
  </xsl:template>

The SDLC_Requests.rowview template is where the action is; this is where I output the actual values from the appropriate list items. I won’t show all of the XSL, but here are some of the interesting pieces.

At the top of the SDLC_Requests.rowview template, I set up some xsl:variables. For each of these three variables, I’m grabbing a value from the parent SDLC_Project’s item based on the ProjectID in the SDLC_Requests item’s ProjectID column. (Again, I’ve repurposed the Title column in the SDLC Projects list for the ProjectID.)

<xsl:template name="SDLC_Requests.rowview">
   <xsl:variable name="Methodology"  select="/dsQueryResponse/SDLC_Projects/Rows/Row[@Title =  current()/@ProjectID]/@Methodology"/>
   <xsl:variable name="Applications"  select="/dsQueryResponse/SDLC_Projects/Rows/Row[@Title =  current()/@ProjectID]/@Applications"/>
   <xsl:variable name="BusinessGroup"  select="/dsQueryResponse/SDLC_Projects/Rows/Row[@Title =  current()/@ProjectID]/@Business_x0020_Group"/>

The next part shows how I output the header for the DVWP. I wanted it to look just like the normal page header, so I used the same CSS class. The call to ddwrt:IfNew lets me display the usual New! Icon if the Request was created recently.

<tr>
   <td  style="padding-left:0px;"  colspan="2">
   <xsl:value-of  select="@Request_x0020_Description"/> (<xsl:value-of  select="@Title"/>)
   <xsl:if  test="ddwrt:IfNew(string(@Created))">
   <IMG  SRC="/_layouts/1033/images/new.gif" alt="New" />
   </xsl:if>
   </td>
  </tr>

Next I start outputting the column values for the Request. For each row, I’m using the same CSS classes which are usually used on the DispForm.aspx page. I like to try to keep the formatting consistent across pages. I’m using conditional formatting for the Methodology column so that I can show a message if the value is blank. (A blank value here is a problem because the artifact requirements are driven by what the Methodology and Request Type values are.) I’m only showing a few of the columns here; the rest of the XSL looks pretty similar.

<tr>
   <td  width="25%">
   Request Type
   </td>
   <td >
   <xsl:value-of  select="@Request_x0020_Type"/>
   </td>
  </tr>
  <tr>
   <td  width="25%">
    Methodology
   </td>
   <td >
   <xsl:choose>
    <xsl:when  test="string-length($Methodology) &gt; 0">
    <xsl:value-of  select="$Methodology"/>
   </xsl:when>
   <xsl:otherwise>
   <i>No  methodology specified.</i>
   </xsl:otherwise>
   </xsl:choose>
   </td>
  </tr>


Finally, I output the links in section B. You’ll see that I’m building up rather complicated links so that I can both pass the values I’ll need on the other end as well as be ensured that I’ll be passed back to this page with the needed values for ProjectID and RequestID when I return.

<tr>
   <td  colspan="99">
   <table  style="width: 100%">
   <tr>
   <td >
   <a  href="Lists/SDLC%20Requests/EditForm.aspx?ID={@ID}&amp;amp;Source={$URL}?ProjectID={$ProjectID}%26RequestID={$RequestID}">Edit  the Request</a>
   </td>
   <td >
   <a  href="Default.aspx?Group={ddwrt:UrlEncode(string($BusinessGroup))}">Go  back to <xsl:value-of select="$BusinessGroup"  disable-output-escaping="yes"/> projects</a>
   </td>
   </tr>
   <tr>
   <td >
    <a  href="/sites/CSO/KR/SDLC%20Artifact%20Repository%202010/Forms/AllArtifacts.aspx?RootFolder=%2fsites%2fCSO%2fKR%2fSDLC%20Artifact%20Repository%202010%2f{translate($RequestID,  ':', '-')}">View All Artifacts for this Request</a>
   </td>
   <td >
   <a  href="ArtifactsMatrix2010.aspx?RequestType={@Request_x0020_Type}&amp;amp;Methodology={$Methodology}&amp;amp;ProjectID={$ProjectID}&amp;amp;RequestID={$RequestID}">View  Artifact Requirements</a>
   </td>
   </tr>
   </table>
   </td>
  </tr>
</xsl:template>
</xsl:stylesheet></Xsl>

In the next article, I’ll show what’s going on in the DVWP in section C, followed by what happens behind the scenes when the Project Manager decides to click on one of the Upload links to add artifacts to the repository.