Friday, November 22, 2013

Use Server Variables In XSLT To Find The Root Site

There’s no way to tell what the site’s Root Web URL will be, nor the protocol used on any given site, so here’s a quick rundown of how to get the Root Web URL using XSLT:

Variable Setup

This example requires these two server variables.
  • SERVER_NAME
  • HTTPS

ServerVariable - SERVER_NAME
Here’s a screenshot of what it looks like in SPD.  With these in place we'll be able to determine the Root Web URL dynamically, so this could be used on any site and the code will know exactly what the URL of the root web is.





XSLT Code

I've commented the key parts to this simple example, so if you want to just take those pieces you can.  Doing this through the GUI, it adds two <ParameterBinding />’s to your code.  Later in the code, you’ll see two variables that are used:
  • httpProtocol
  • sitePath
These variables are put together to form the URL of the Root Web.

<WebPartPages:DataFormWebPart runat="server" Description="" PartOrder="2" HelpLink="" AllowRemove="True" IsVisible="True" AllowHide="True" UseSQLDataSourcePaging="True" ExportControlledProperties="True" DataSourceID="" Title="" ViewFlag="8" NoDefaultStyle="TRUE" AllowConnect="True" FrameState="Normal" PageSize="-1" PartImageLarge="" AsyncRefresh="True" ExportMode="All" Dir="Default" DetailLink="" ShowWithSampleData="True" ListId="" ListName="" FrameType="None" PartImageSmall="" IsIncluded="True" SuppressWebPartChrome="False" AllowEdit="True" ManualRefresh="False" ChromeType="None" AutoRefresh="False" AutoRefreshInterval="60" AllowMinimize="True" ViewContentTypeId="" InitialAsyncDataFetch="False" MissingAssembly="Cannot import this Web Part." HelpMode="Modeless" ID="" ConnectionID="00000000-0000-0000-0000-000000000000" AllowZoneChange="True" IsIncludedFilter="" __MarkupType="vsattributemarkup" __WebPartId="" __AllowXSLTEditing="true" WebPart="true" Height="" Width=""><ParameterBindings>
                        <ParameterBinding Name="dvt_apos" Location="Postback;Connection"/>
                        <ParameterBinding Name="ManualRefresh" Location="WPProperty[ManualRefresh]"/>
                        <ParameterBinding Name="UserID" Location="CAMLVariable" DefaultValue="CurrentUserName"/>
                        <ParameterBinding Name="Today" Location="CAMLVariable" DefaultValue="CurrentDate"/>
                       
                        <!-- Set these two variables up within your DVWP/XLV Web Part -->
                        <ParameterBinding Name="HTTPS" Location="ServerVariable(HTTPS)" DefaultValue=""/>
                        <ParameterBinding Name="SERVER_NAME" Location="ServerVariable(SERVER_NAME)" DefaultValue=""/>
                    </ParameterBindings>
<DataFields>
@myField,myField;</DataFields>
<Xsl>
    <xsl:stylesheet xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" 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">
        <xsl:output method="html" indent="no"/>
        <xsl:decimal-format NaN=""/>
        <xsl:param name="dvt_apos">&apos;</xsl:param>
        <xsl:param name="ManualRefresh"></xsl:param>
        <xsl:param name="webUrl" />
        <xsl:param name="HTTPS" />
        <xsl:param name="SERVER_NAME" />
        <xsl:variable name="dvt_1_automode">0</xsl:variable>
        <xsl:template match="/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" 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="dvt_1"/>
        </xsl:template>
        <xsl:template name="dvt_1">                       <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row"/>
            <xsl:variable name="dvt_RowCount" select="count($Rows)"/>
            <xsl:variable name="IsEmpty" select="$dvt_RowCount = 0" />
            <xsl:variable name="dvt_IsEmpty" select="$dvt_RowCount = 0"/>
            <!-- Use this variable to determine the protocol used by the site -->
            <xsl:variable name="httpProtocol">
                <xsl:choose>
                    <xsl:when test="normalize-space($HTTPS) = 'on'">https://</xsl:when>
                    <xsl:otherwise>http://</xsl:otherwise>
                </xsl:choose>
            </xsl:variable>
            <!-- This variable concats all of these variables together -->
            <!-- Example sitePath output: http://iOnline247.me/ -->
            <xsl:variable name="sitePath">
                <xsl:value-of select="string(normalize-space(concat(concat(normalize-space($httpProtocol), normalize-space($SERVER_NAME)), '/')))" />
            </xsl:variable>   
                   
                    <!-- {...SNIP...} -->
        </xsl:template>
    </xsl:stylesheet>
</Xsl>

The end result is a nice URL.  Here’s a sample output: http://iOnline247.me/
I'm not sure why the protocol isn't straightforward to get.  I may have missed something obvious, but I didn't see any other way of determining the protocol.  If there's an easier way, feel free to let me know.

Monday, November 18, 2013

Add SublimeText to Windows Context Menu

Since I've made the switch, I've loved SublimeText.  It is by far superior to Notepad++.  There are some small things that I'm still working out, but I'm sure with time they'll go away.  One of those things was the context menu was missing.  I'm sure this was due to me using the portable installer, but it's a must for me to have!  Doing a simple search on the web, I came across this gist that does exactly what I needed:

Here's the code in its entirety... You just never know with the web. ;)

@echo off
SET st2Path=C:\Program Files\Sublime Text 2\sublime_text.exe
 
rem add it for all file types
@reg add "HKEY_CLASSES_ROOT\*\shell\Open with Sublime Text 2"         /t REG_SZ /v "" /d "Open with Sublime Text 2"   /f
@reg add "HKEY_CLASSES_ROOT\*\shell\Open with Sublime Text 2"         /t REG_EXPAND_SZ /v "Icon" /d "%st2Path%,0" /f
@reg add "HKEY_CLASSES_ROOT\*\shell\Open with Sublime Text 2\command" /t REG_SZ /v "" /d "%st2Path% \"%%1\"" /f
 
rem add it for folders
@reg add "HKEY_CLASSES_ROOT\Folder\shell\Open with Sublime Text 2"         /t REG_SZ /v "" /d "Open with Sublime Text 2"   /f
@reg add "HKEY_CLASSES_ROOT\Folder\shell\Open with Sublime Text 2"         /t REG_EXPAND_SZ /v "Icon" /d "%st2Path%,0" /f
@reg add "HKEY_CLASSES_ROOT\Folder\shell\Open with Sublime Text 2\command" /t REG_SZ /v "" /d "%st2Path% \"%%1\"" /f
pause

Tuesday, October 1, 2013

Simple[r] jQuery Content Filter

A post titled: Simple jQuery Content Filter for Office 365 Public Website by Doug Hemminger came by my twitter stream that caught my eye.  A real quick win and all around goodness for everyone.  Reading through, I noticed some things I could tidy up.  So I asked him if he'd be okay if I re-factor the code that he blogged about and he said absolutely!

UX

It's almost expected these days to be able to click the text next to a checkbox and have it just work.  Doug is using <span> for the text, so changing this to use <label> and wrap the <input> is a real quick win.

As I was writing this up, I also had another idea...  Why not have all of the filters show no matter how far down you scroll?  This is also a quick and easy win, so it's in there too.

DRY

DO NOT REPEAT YOURSELF! 
I'm totally guilty of this, but I try to do my best every time.  I checked the source of the page where the code was added and found:
        $(document).ready(function () {
            function SPSToggleView() {
                if (!$("#SPSBusinessCheckbox").prop("checked")) {
                    $(".SPSBusiness").hide();
                } else {
                    $(".SPSBusiness").show();
                }

                if (!$("#SPSInformationWorkerCheckbox").prop("checked")) {
                    $(".SPSInformationWorker").hide();
                } else {
                    $(".SPSInformationWorker").show();
                }

                if (!$("#SPSCertificationCheckbox").prop("checked")) {
                    $(".SPSCertification").hide();
                } else {
                    $(".SPSCertification").show();
                }

                if (!$("#SPSBusinessIntelligenceCheckbox").prop("checked")) {
                    $(".SPSBusinessIntelligence").hide();
                } else {
                    $(".SPSBusinessIntelligence").show();
                }

                if (!$("#SPSDeveloperCheckbox").prop("checked")) {
                    $(".SPSDeveloper").hide();
                } else {
                    $(".SPSDeveloper").show();
                }
                if (!$("#SPSITProCheckbox").prop("checked")) {
                    $(".SPSITPro").hide();
                } else {
                    $(".SPSITPro").show();
                }
                if (!$("#SPSCloudCheckbox").prop("checked")) {
                    $(".SPSCloud").hide();
                } else {
                    $(".SPSCloud").show();
                }
                if (!$("#SPSSocialCheckbox").prop("checked")) {
                    $(".SPSSocial").hide();
                } else {
                    $(".SPSSocial").show();
                }
                if (!$("#SPSGeneralCheckbox").prop("checked")) {
                    $(".SPSGeneral").hide();
                } else {
                    $(".SPSGeneral").show();
                }
            };
            $("#SPSInformationWorkerCheckbox").attr("checked", true);
            $("#SPSBusinessCheckbox").attr("checked", true);
            $("#SPSCertificationCheckbox").attr("checked", true);
            $("#SPSBusinessIntelligenceCheckbox").attr("checked", true);
            $("#SPSDeveloperCheckbox").attr("checked", true);
            $("#SPSITProCheckbox").attr("checked", true);
            $("#SPSCloudCheckbox").attr("checked", true);
            $("#SPSSocialCheckbox").attr("checked", true);
            $("#SPSGeneralCheckbox").attr("checked", true);

            $("#SPSInformationWorkerCheckbox").click(SPSToggleView);
            $("#SPSBusinessCheckbox").click(SPSToggleView);
            $("#SPSCertificationCheckbox").click(SPSToggleView);
            $("#SPSBusinessIntelligenceCheckbox").click(SPSToggleView);
            $("#SPSDeveloperCheckbox").click(SPSToggleView);
            $("#SPSITProCheckbox").click(SPSToggleView);
            $("#SPSCloudCheckbox").click(SPSToggleView);
            $("#SPSSocialCheckbox").click(SPSToggleView);
            $("#SPSGeneralCheckbox").click(SPSToggleView);
        });


Even though this is a simple solution, I knew it could be made simpler. :) I got it down to this:

    
$(document).ready(function () {
        var $wrapper = $("#wrapper");

        $wrapper.on("change", "input[data-filter]", function (event) {
            var $this = $(this),
                filter = $this.data("filter");

            $wrapper.find("div[data-filter='" + filter + "']").slideToggle();
        });
    });

The magic is done by hiding the value(s) we are going to hide/show directly onto the <input> as a `data-filter` attribute.  When the change event is fired, that value is surfaced.  Then a simple query of the DOM to find the correct <div>'s that this filter relates to.  The jQuery#slideToggle method knows whether or not the elements are hidden or displayed, so there's no need to check the state of the <input>.

Results

Thursday, March 21, 2013

RowLimit and CAML Query

I keep forgetting this, so here's a little note for me:

"<View Scope='RecursiveAll'><Query><Where><Eq><FieldRef Name='Author'/><Value Type='Integer'><UserID/></Value></Eq></Where><OrderBy><FieldRef Name='Created' Ascending='False' /></OrderBy></Query><RowLimit>1</RowLimit></View>"

There you have it... A valid usage of RowLimit within a CAML Query.

Thursday, March 14, 2013

jQueryUI Autocomplete with SharePoint Drop Downs

SharePoint Drop Downs Suck!

I can't go anywhere without having this UX nightmare on my hands.  How many times have you started typing in these drop downs, then tab away expecting the same UX you have, LIKE EVERYWHERE ELSE ON THE INTERNET?  The problem is, these drop downs are inconsistent in their behavior and even render completely different in Internet Explorer. If there are 20+ items within the list, you can guarantee calls to your helpdesk.  I've spent enough time working on this problem; enough to offer up some code so you can handle this yourself too.  Hopefully, you find it useful.

Search by Type UX

This has been blogged about a bunch... Consider this horse completely beaten and dead at this point, but nothing I've seen out there handles SharePoint drop downs the way I do with this code.  Without further adieu, the codez:



<link type="text/css" rel="stylesheet" href="//code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.min.css">

<script src="//code.jquery.com/jquery-1.8.3.min.js"></script>
<script src="//code.jquery.com/ui/1.10.2/jquery-ui.min.js"></script>

<script>
function getFormDDL( columnName ) {
var $ddl = $("select[title='" + columnName + "']"),
choices,
choiceArray,
index = 1
; //local vars
if ( $ddl.length > 0 ) {
$ddl.SPOptions = $ddl.find("option").map(function() {
var $el = $(this);
return {
label: $el.text(),
id: $el.val()
}; 
}).get();
$ddl.SPComplexDdl = false;
} else {
$ddl = $("input[title='" + columnName + "']");
choices = $ddl.attr("choices");
choiceArray = choices.split("|");
$ddl.SPOptions = [];
for ( ; index < choiceArray.length; index = index + 2 ) {
$ddl.SPOptions.push({
label: choiceArray[ index - 1 ],
id: choiceArray[ index ]
});
}
$ddl.SPComplexDdl = true;
}
return $ddl;
}
function setFormDdl( $ddl, lookupVal ) {
var choices,
choiceArray,
hiddenInput,
index
;

if ( $ddl.SPComplexDdl ) {
choices = $ddl.attr("choices");
hiddenInput = $ddl.attr("optHid");
$("input[id='" + hiddenInput + "']").val( lookupVal );

choiceArray = choices.split("|");
for ( index = 1; index < choiceArray.length; index = index + 2 ) {
if ( choiceArray[ index ] == lookupVal ) {
$ddl.val( choiceArray[ index - 1 ] );
}
}
} else {
$ddl.val( lookupVal );
}
}
/*****
*
* Main processing
*
******/

$( document ).ready(function() {
                // To use this, just add as many Display Names you want that are Drop Downs on your form!
var columnNames = [ "Full Name", "City" ]
;

// Don't touch... It works as is. :)
$.each( columnNames, function( index, columnName ) {
var $ddl = getFormDDL( columnName ),
$wrapper = $("<div class='sp-planet-autocomplete'>"),
idName = columnName.replace(/ /g, "-"),
$input = $("<input class='ms-long' id='" + idName + "' />")
; //local vars
// debugger;
// When ddl is complex, we need to hide the whole shebang.  Might as well do it for a simple ddl too.
$ddl.closest("span[dir='none']").hide();
$wrapper.append( $input );
$ddl.closest("td").prepend( $wrapper );
// Find autocomplete options here.
// http://jqueryui.com/autocomplete/
$input.autocomplete({
source: $ddl.SPOptions,
        minLength: 0,
          select: function( event, ui ) {
setFormDdl( $ddl, ui.item.id );
}
});

// If selection is made and then text is deleted, this will prevent that. This ain't my first rodeo...
$input.on("blur", function( el ) {
// debugger;
if( el.currentTarget.value.trim() === "" ) {
setFormDdl( $ddl, 0 );
}
});
});
});

</script>


SharePoint Drop Downs = Search by Type Now!

Now this is a cool alternative to the standard ho-hum.  It's safe to say that users know exactly what they are looking for when there are 20+ options to choose from.  With that in mind, providing a search by type UX feels natural.  Also as an added bonus, jQueryUI gives you: up and down keyboard navigation and enter/tab selection.  Having this enabled just FEELS good!

Search by Type Usage

The columNames variable is the only variable you have to change.  Everything else is handled by jQuery/jQueryUI magic under the hood.  The only caveat to my code is that it currently doesn't handle multiple selections.  My current needs haven't forced me to pursue this, but if you find it necessary, I may revisit it.  I'd be more inclined to do so, *if* it was profitable. :-)

Feel free to let me know how this code works out for you.  It *should* just work.

Tuesday, February 12, 2013

Setting field values using CSOM client side - Another look

Last night after hitting publish, I enjoyed a long ride home on the metro...  I was able to catch up on some of my reading.  It's a relaxing part of my day and sometimes exciting because I get to grind away on concepts that I'm working on.  Last night did not disappoint.

spUtils - setColumnVal


As I said in the previous post, I've already tackled this problem, however, I didn't really like the implementation.  So, here's my bright idea... Toggle the library back to use setColumnVal and see what the XML looks like under the hood.  Doing just that, here's what's produced( I've snipped this for brevity ):


<Method Name="SetFieldValue" Id="26" ObjectPathId="21">
  <Parameters>
<Parameter Type="String">AssignedTo</Parameter>
<Parameter Type="Array">
<Object TypeId="{c956ab54-16bd-4c18-89d2-996f57282a6f}">
<Property Name="LookupValue" Type="String">DEV\Administrator</Property>
<Property Name="LookupId" Type="Number">-1</Property>
</Object>
<Object TypeId="{c956ab54-16bd-4c18-89d2-996f57282a6f}">
<Property Name="LookupValue" Type="String">DEV\spUser</Property>
<Property Name="LookupId" Type="Number">-1</Property>
</Object>
</Parameter>
</Method>


So based on that, it's easy to see the people picker XML has to be an array of objects.  Let's give that a shot now using this code mixed with parseAndSetFieldValue.

spUtils - parseAndSetFieldValue revisited


spUtils.updateListItems({
listName: "spUtils",
updates : {
1 : {
"Title" : spUtils.isoDate(),
"AssignedTo" : [
{
LookupValue: "DEV\\Administrator",
LookupId: -1
},
{
LookupValue: "DEV\\spUser",
LookupId: -1
}
]
}
},
success: function( data, ctx ) {
debugger;
}
});

Using the code above produces this XML ( snipped as well for brevity ):

<Method Name="ParseAndSetFieldValue" Id="44" ObjectPathId="21">
  <Parameters>
<Parameter Type="String">AssignedTo</Parameter>
<Parameter Type="Array">
<Object Type="Dictionary">
<Property Name="LookupValue" Type="String">DEV\Administrator</Property>
<Property Name="LookupId" Type="Number">-1</Property>
</Object>
<Object Type="Dictionary">
<Property Name="LookupValue" Type="String">DEV\spUser</Property>
<Property Name="LookupId" Type="Number">-1</Property>
</Object>
</Parameter>
  </Parameters>
</Method>


It's remarkably close to the XML that actually works.  The only thing that is different is the Object Type.  Sadly, this is all that it takes for this to FAIL.  Yep, that's right...  Trying to be smarter than the average bear, let's give it another shake.  This time, I'm going to take some code out of the setColumnVal method and drop it into an array.  Take a look at this:

spUtils.updateListItems({
listName: "spUtils",
updates : {
1 : {
"Title" : spUtils.isoDate(),
"AssignedTo" : [  SP.FieldUserValue.fromUser("DEV\\Administrator"),
  SP.FieldUserValue.fromUser("DEV\\spUser")
]
}
},
success: function( data, ctx ) {
debugger;
}
});


This in turn produces XML that *should* work!

<Method Name="ParseAndSetFieldValue" Id="44" ObjectPathId="21">
  <Parameters>
  <Parameter Type="String">AssignedTo</Parameter>
  <Parameter Type="Array">
  <Object TypeId="{c956ab54-16bd-4c18-89d2-996f57282a6f}">
  <Property Name="LookupValue" Type="String">DEV\Administrator</Property>
<Property Name="LookupId" Type="Number">-1</Property>
  </Object>
  <Object TypeId="{c956ab54-16bd-4c18-89d2-996f57282a6f}">
<Property Name="LookupValue" Type="String">DEV\spUser</Property>
<Property Name="LookupId" Type="Number">-1</Property>
  </Object>
  </Parameter>
  </Parameters>
</Method>


The only difference this time is the Method Name attribute.  Sadly, even this FAILS! I was going to continue with using numbers, but with this being a show stopper, I'm convinced I've researched this thoroughly enough.  This may be different in SP2013, it simply doesn't work in SP2010, therefore unreliable.

What's next?


Since I need the context of the list item to set its values when using the .update() method, it's not feasible to change what I have currently.  To set lookups and people picker values in CSOM, you have to use the code I've already written.  Guess it's time I start documenting the API, eh?

Monday, February 11, 2013

Setting field values using CSOM client side

A rather long time ago, I wrote some code to handle creation and updates of items when using Client Object Model.  This bit of code works very well however it's always made me feel like the API I've built had warts. Take a look for yourself.  This method allows for anyone to modify Lookup values as well as People Picker values.  Using this, you can also set multiple values without a problem.  That's the good...  The ugly is for this to work, it's necessary to tell spUtils the column type.  This ended up looking something like this:


/***************************************
Test 26 ~ updateListItems - Updates item's lookup and people picker column
***************************************/
spUtils.updateListItems({
listName : "Project Tasks",
updates : {
111 : { // the key is the item ID
"RelatedProject{L}" : spUtils.isoDate(),
"AssignedTo{P}" : 1,

     "Title" : "Hello, World!"
}
},
success : function() { debugger; }
});


// End codez


Notice the appended characters that represent the column type. This is fine but at the end of the day not very user friendly...  I've always thought I could do better.  This blog post will explore what I've found inside the SP Namespace and what my options are to fix it.

SP.Debug.js to the Rescue?

When looking at a listItem object in my debugger, I found a rather coy method.  It literally screamed at me: "Put me in the game coach!"... Totally looked over this one when building CRUD into spUtils.  With that said, I've made the change under the hood to use this method instead.

parseAndSetFieldValue: function(fieldName, value_) {ULS5Vl:;
        this.get_fieldValues()[fieldName] = value_;
        var $v_0 = new SP.ClientActionInvokeMethod(this, 'ParseAndSetFieldValue', [ fieldName, value_ ]);
        if ((this.get_context())) {
            this.get_context().addQuery($v_0);
        }
},


For posterity, here's the raw bits of the function call. Nothing too exciting here since it's really a wrapper for the ClientActionInvokeMethod. Come to think of it, what isn't a wrapper for ClientActionInvokeMethod inside SP.js?

Is parseAndSetFieldValue really up to snuff?

For me it was truly a magical moment coming across this method.  As of matter of fact, I made a note of it when I did to come back someday and put the spotlight directly on it...  As of a result, here are my findings in raw format ( I've highlighted the important pieces of XML ):


<!-- Test 1
[ "AssignedTo", ["DEV\\Administrator", "DEV\\spuser"] ]
XML sent to Server using Array of strings
-->

<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" SchemaVersion="14.0.0.0" LibraryVersion="14.0.4762.1000" ApplicationName="Javascript Library">
<Actions>
<ObjectPath Id="1" ObjectPathId="0" />
<ObjectPath Id="3" ObjectPathId="2" />
<ObjectPath Id="5" ObjectPathId="4" />
<ObjectPath Id="7" ObjectPathId="6" />
<ObjectIdentityQuery Id="8" ObjectPathId="6" />
<ObjectPath Id="10" ObjectPathId="9" />
<Method Name="ParseAndSetFieldValue" Id="11" ObjectPathId="9">
<Parameters>
<Parameter Type="String">Title</Parameter>
<Parameter Type="String">2013-02-11T20:10:13Z</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="12" ObjectPathId="9" />
<Query Id="13" ObjectPathId="9">
<Query SelectAllProperties="false">
<Properties>
<Property Name="Title" ScalarProperty="true" />
</Properties>
</Query>
</Query>
<Method Name="ParseAndSetFieldValue" Id="14" ObjectPathId="9">
<Parameters>
<Parameter Type="String">AssignedTo</Parameter>
<Parameter Type="Array">
<Object Type="String">DEV\Administrator</Object>
<Object Type="String">DEV\spuser</Object>
</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="15" ObjectPathId="9" />
<Query Id="16" ObjectPathId="9">
<Query SelectAllProperties="false">
<Properties>
<Property Name="Title" ScalarProperty="true" />
<Property Name="AssignedTo" ScalarProperty="true" />
</Properties>
</Query>
</Query>
<Method Name="ParseAndSetFieldValue" Id="17" ObjectPathId="9">
<Parameters>
<Parameter Type="String">RelatedProject</Parameter>
<Parameter Type="Number">2</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="18" ObjectPathId="9" />
<Query Id="19" ObjectPathId="9">
<Query SelectAllProperties="false">
<Properties>
<Property Name="Title" ScalarProperty="true" />
<Property Name="AssignedTo" ScalarProperty="true" />
<Property Name="RelatedProject" ScalarProperty="true" />
</Properties>
</Query>
</Query>
</Actions>
<ObjectPaths>
<StaticProperty Id="0" TypeId="{3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a}" Name="Current" />
<Property Id="2" ParentId="0" Name="Web" />
<Property Id="4" ParentId="2" Name="Lists" />
<Method Id="6" ParentId="4" Name="GetByTitle">
<Parameters>
<Parameter Type="String">Project Tasks</Parameter>
</Parameters>
</Method>
<Method Id="9" ParentId="6" Name="GetItemById">
<Parameters>
<Parameter Type="Number">42</Parameter>
</Parameters>
</Method>
</ObjectPaths>
</Request>

Result:
Request failed. Invalid look-up value. A look-up field contains invalid data. Please check the value and try again. 


<!-- Test 2 
[ "AssignedTo", "DEV\\Administrator, DEV\\spuser" ]
XML sent to Server using comma separated values inside a string.
-->

<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" SchemaVersion="14.0.0.0" LibraryVersion="14.0.4762.1000" ApplicationName="Javascript Library">
<Actions>
<ObjectPath Id="1" ObjectPathId="0" />
<ObjectPath Id="3" ObjectPathId="2" />
<ObjectPath Id="5" ObjectPathId="4" />
<ObjectPath Id="7" ObjectPathId="6" />
<ObjectIdentityQuery Id="8" ObjectPathId="6" />
<ObjectPath Id="10" ObjectPathId="9" />
<Method Name="ParseAndSetFieldValue" Id="11" ObjectPathId="9">
<Parameters>
<Parameter Type="String">Title</Parameter>
<Parameter Type="String">2013-02-11T20:16:15Z</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="12" ObjectPathId="9" />
<Query Id="13" ObjectPathId="9">
<Query SelectAllProperties="false">
<Properties>
<Property Name="Title" ScalarProperty="true" />
</Properties>
</Query>
</Query>
<Method Name="ParseAndSetFieldValue" Id="14" ObjectPathId="9">
<Parameters>
<Parameter Type="String">AssignedTo</Parameter>
<Parameter Type="String">DEV\Administrator; DEV\spuser</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="15" ObjectPathId="9" />
<Query Id="16" ObjectPathId="9">
<Query SelectAllProperties="false">
<Properties>
<Property Name="Title" ScalarProperty="true" />
<Property Name="AssignedTo" ScalarProperty="true" />
</Properties>
</Query>
</Query>
</Actions>
<ObjectPaths>
<StaticProperty Id="0" TypeId="{3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a}" Name="Current" />
<Property Id="2" ParentId="0" Name="Web" />
<Property Id="4" ParentId="2" Name="Lists" />
<Method Id="6" ParentId="4" Name="GetByTitle">
<Parameters>
<Parameter Type="String">Project Tasks</Parameter>
</Parameters>
</Method>
<Method Id="9" ParentId="6" Name="GetItemById">
<Parameters>
<Parameter Type="Number">42</Parameter>
</Parameters>
</Method>
</ObjectPaths>
</Request>

Result:
Request failed. Invalid data has been used to update the list item. The field you are trying to update may be read only. 

<!-- Test 3
[ "AssignedTo", "DEV\\Administrator;#DEV\\spuser" ]
XML sent to Server using the old semi-colon bang delimiter.
-->

<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" SchemaVersion="14.0.0.0" LibraryVersion="14.0.4762.1000" ApplicationName="Javascript Library">
<Actions>
<ObjectPath Id="1" ObjectPathId="0" />
<ObjectPath Id="3" ObjectPathId="2" />
<ObjectPath Id="5" ObjectPathId="4" />
<ObjectPath Id="7" ObjectPathId="6" />
<ObjectIdentityQuery Id="8" ObjectPathId="6" />
<ObjectPath Id="10" ObjectPathId="9" />
<Method Name="ParseAndSetFieldValue" Id="11" ObjectPathId="9">
<Parameters>
<Parameter Type="String">Title</Parameter>
<Parameter Type="String">2013-02-11T20:28:42Z</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="12" ObjectPathId="9" />
<Query Id="13" ObjectPathId="9">
<Query SelectAllProperties="false">
<Properties>
<Property Name="Title" ScalarProperty="true" />
</Properties>
</Query>
</Query>
<Method Name="ParseAndSetFieldValue" Id="14" ObjectPathId="9">
<Parameters>
<Parameter Type="String">AssignedTo</Parameter>
<Parameter Type="String">DEV\Administrator;#DEV\spuser</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="15" ObjectPathId="9" />
<Query Id="16" ObjectPathId="9">
<Query SelectAllProperties="false">
<Properties>
<Property Name="Title" ScalarProperty="true" />
<Property Name="AssignedTo" ScalarProperty="true" />
</Properties>
</Query>
</Query>
</Actions>
<ObjectPaths>
<StaticProperty Id="0" TypeId="{3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a}" Name="Current" />
<Property Id="2" ParentId="0" Name="Web" />
<Property Id="4" ParentId="2" Name="Lists" />
<Method Id="6" ParentId="4" Name="GetByTitle">
<Parameters>
<Parameter Type="String">Project Tasks</Parameter>
</Parameters>
</Method>
<Method Id="9" ParentId="6" Name="GetItemById">
<Parameters>
<Parameter Type="Number">42</Parameter>
</Parameters>
</Method>
</ObjectPaths>
</Request>

Result:
Request failed. Invalid look-up value.  A look-up field contains invalid data. Please check the value and try again. 

<!-- Test 4 
[ "AssignedTo", "DEV\\Administrator; DEV\\spuser" ]
XML sent to Server using text similar to typing into the control manually.
-->

<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" SchemaVersion="14.0.0.0" LibraryVersion="14.0.4762.1000" ApplicationName="Javascript Library">
<Actions>
<ObjectPath Id="1" ObjectPathId="0" />
<ObjectPath Id="3" ObjectPathId="2" />
<ObjectPath Id="5" ObjectPathId="4" />
<ObjectPath Id="7" ObjectPathId="6" />
<ObjectIdentityQuery Id="8" ObjectPathId="6" />
<ObjectPath Id="10" ObjectPathId="9" />
<Method Name="ParseAndSetFieldValue" Id="11" ObjectPathId="9">
<Parameters>
<Parameter Type="String">Title</Parameter>
<Parameter Type="String">2013-02-11T20:32:14Z</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="12" ObjectPathId="9" />
<Query Id="13" ObjectPathId="9">
<Query SelectAllProperties="false">
<Properties>
<Property Name="Title" ScalarProperty="true" />
</Properties>
</Query>
</Query>
<Method Name="ParseAndSetFieldValue" Id="14" ObjectPathId="9">
<Parameters>
<Parameter Type="String">AssignedTo</Parameter>
<Parameter Type="String">DEV\Administrator; DEV\spuser</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="15" ObjectPathId="9" />
<Query Id="16" ObjectPathId="9">
<Query SelectAllProperties="false">
<Properties>
<Property Name="Title" ScalarProperty="true" />
<Property Name="AssignedTo" ScalarProperty="true" />
</Properties>
</Query>
</Query>
</Actions>
<ObjectPaths>
<StaticProperty Id="0" TypeId="{3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a}" Name="Current" />
<Property Id="2" ParentId="0" Name="Web" />
<Property Id="4" ParentId="2" Name="Lists" />
<Method Id="6" ParentId="4" Name="GetByTitle">
<Parameters>
<Parameter Type="String">Project Tasks</Parameter>
</Parameters>
</Method>
<Method Id="9" ParentId="6" Name="GetItemById">
<Parameters>
<Parameter Type="Number">42</Parameter>
</Parameters>
</Method>
</ObjectPaths>
</Request>

Result:
Request failed. Invalid data has been used to update the list item. The field you are trying to update may be read only. 


<!-- Test 5
[ "AssignedTo", 1 ]
XML sent to Server using a single user id.  Works as a string as well.
-->

<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" SchemaVersion="14.0.0.0" LibraryVersion="14.0.4762.1000" ApplicationName="Javascript Library">
<Actions>
<ObjectPath Id="1" ObjectPathId="0" />
<ObjectPath Id="3" ObjectPathId="2" />
<ObjectPath Id="5" ObjectPathId="4" />
<ObjectPath Id="7" ObjectPathId="6" />
<ObjectIdentityQuery Id="8" ObjectPathId="6" />
<ObjectPath Id="10" ObjectPathId="9" />
<Method Name="ParseAndSetFieldValue" Id="11" ObjectPathId="9">
<Parameters>
<Parameter Type="String">Title</Parameter>
<Parameter Type="String">2013-02-11T20:37:00Z</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="12" ObjectPathId="9" />
<Query Id="13" ObjectPathId="9">
<Query SelectAllProperties="false">
<Properties>
<Property Name="Title" ScalarProperty="true" />
</Properties>
</Query>
</Query>
<Method Name="ParseAndSetFieldValue" Id="14" ObjectPathId="9">
<Parameters>
<Parameter Type="String">AssignedTo</Parameter>
<Parameter Type="Number">1</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="15" ObjectPathId="9" />
<Query Id="16" ObjectPathId="9">
<Query SelectAllProperties="false">
<Properties>
<Property Name="Title" ScalarProperty="true" />
<Property Name="AssignedTo" ScalarProperty="true" />
</Properties>
</Query>
</Query>
</Actions>
<ObjectPaths>
<StaticProperty Id="0" TypeId="{3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a}" Name="Current" />
<Property Id="2" ParentId="0" Name="Web" />
<Property Id="4" ParentId="2" Name="Lists" />
<Method Id="6" ParentId="4" Name="GetByTitle">
<Parameters>
<Parameter Type="String">Project Tasks</Parameter>
</Parameters>
</Method>
<Method Id="9" ParentId="6" Name="GetItemById">
<Parameters>
<Parameter Type="Number">42</Parameter>
</Parameters>
</Method>
</ObjectPaths>
</Request>

Result:
Holy shit! It works....


<!-- Test 6
[ "AssignedTo", [ 1, 17 ] ]
XML sent to Server using an array of numbers.
-->

<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" SchemaVersion="14.0.0.0" LibraryVersion="14.0.4762.1000" ApplicationName="Javascript Library">
<Actions>
<ObjectPath Id="1" ObjectPathId="0" />
<ObjectPath Id="3" ObjectPathId="2" />
<ObjectPath Id="5" ObjectPathId="4" />
<ObjectPath Id="7" ObjectPathId="6" />
<ObjectIdentityQuery Id="8" ObjectPathId="6" />
<ObjectPath Id="10" ObjectPathId="9" />
<Method Name="ParseAndSetFieldValue" Id="11" ObjectPathId="9">
<Parameters>
<Parameter Type="String">Title</Parameter>
<Parameter Type="String">2013-02-11T20:40:36Z</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="12" ObjectPathId="9" />
<Query Id="13" ObjectPathId="9">
<Query SelectAllProperties="false">
<Properties>
<Property Name="Title" ScalarProperty="true" />
</Properties>
</Query>
</Query>
<Method Name="ParseAndSetFieldValue" Id="14" ObjectPathId="9">
<Parameters>
<Parameter Type="String">AssignedTo</Parameter>
<Parameter Type="Array">
<Object Type="Number">17</Object>
<Object Type="Number">1</Object>
</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="15" ObjectPathId="9" />
<Query Id="16" ObjectPathId="9">
<Query SelectAllProperties="false">
<Properties>
<Property Name="Title" ScalarProperty="true" />
<Property Name="AssignedTo" ScalarProperty="true" />
</Properties>
</Query>
</Query>
</Actions>
<ObjectPaths>
<StaticProperty Id="0" TypeId="{3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a}" Name="Current" />
<Property Id="2" ParentId="0" Name="Web" />
<Property Id="4" ParentId="2" Name="Lists" />
<Method Id="6" ParentId="4" Name="GetByTitle">
<Parameters>
<Parameter Type="String">Project Tasks</Parameter>
</Parameters>
</Method>
<Method Id="9" ParentId="6" Name="GetItemById">
<Parameters>
<Parameter Type="Number">42</Parameter>
</Parameters>
</Method>
</ObjectPaths>
</Request>

Result:
Well, it doesn't fail... However the column is set to null.


Well that just sucks now doesn't it?  Using that method, I can ONLY use the user ID AND ONLY one user ID.  What a waste of time.  The method should really be called: parseValuesAndWasteMatthewsTime().

What about GetList?

I could query the current site, cache all of the list information and then figure out the column types on the fly.
While I know I could pull this off, I really don't think it's a great option.  CSOM is asynchronous in nature, which means I'd have to nest everything AFTER the initial call to get the list information.  That would be worse than what is already there...

What's next?

Since this inefficiency cannot be overcome in SP2010, I'm going to press on with the API I currently have because it simply works...  I'll continue to look for better ways to do this, as this is primarily why I've never documented spUtils.  I figured the API would change once I found a different way and didn't want to deal with the overhead of dealing with that.

Well since I feel like I've given the SP Namespace a fair shake, it's time to clean up the code and start vetting it for usage within SP2013.  Look for much more capabilities to be baked into this coming soon!

Example: spUtils.startWorkflow();  // This will not use web services to accomplish this. I'm trying to rely on SP.js for everything.

Happy coding!