V10 Actions work for some, “Object reference” errors for others

I wanted to share a tidbit I encountered when working with a client. We were attempting to troubleshoot a V10 Action – specifically, a Create Item action that was working for the person who built it.  Other users that tried to run the Action received an error in the Action Form stating “Object reference not set to an instance of an object”. This error also occurred if the same users tried to access/edit the Action via the Actions Wizard.

Figuring this had to be a permissions issue, we checked the following:

  • · Did the user have access to the Actions Library?  Yes, they did.
  • · Did the user have access to the individual Action in said library? Yes, they did.
  • · Did the user have Add rights on the list the Action was configured to run against?  It shouldn’t be necessary but we looked anyway and they did have rights.
  • · The Action itself and the CorasWorks Central View it was attached to were stored in another site collection, so we made doubly-sure they had Read rights there as well.  They did.
  • · The list/Action form contained a CorasWorks Toolset Workplace Lookup (WPL); perhaps the user’s didn’t have Read rights on the WPL Provider, but they did.

At this point, I was a little bit stumped and not sure where else the user’s would need permissions.  To be sure, we bumped them up to Site Owners on both the site collection they were working in as well as the site collection where the Central Views and Actions were stored – even that didn’t work.

Then it hit me.

They were using a Global Link to configure the Action; specifically, a Global Link to point to the specific list the Action should create the new item within.  The Global Links are stored in a list that is automatically created when enabling Central Configuration via Central Administration.  Our customer wanted the Central Configuration site to be a secured site for him and other CorasWorks-trained Administrators only.  His general users did not have Read rights to the Central Configuration site; as such, they weren’t able to read the Global Links list found within.

In the end it was a perfectly reasonable, and soon to be common, configuration – it just wasn’t part of my usual permissions check when something didn’t work.  So, lesson learned: if you see that error when trying to run an Action but the user has sufficient rights (in our case, Admin rights) on everything you think to check, don’t forget to look at the Global Links themselves and if the user has access to those!

Extend the EDP To Track File Downloads

The possibilities for creating solutions with the Data Integration Toolset are almost limitless.  With this in mind, one idea for creativity is tracking file downloads from SharePoint with some simple settings of the External Data Provider.   

Let’s set up our scenario: A software company, SuperSoft stores and manages their software releases in SharePoint libraries and they give their customers access to these downloads based on their level of purchase.  What SuperSoft currently doesn’t support internally is the tracking of who is actually downloading the software.  EDP to the rescue! 

To solve SuperSoft’s problem, we’ll first have them setup a Custom List in SharePoint where we’ll store the visitor’s information, we’ll create 3 columns: User, DownloadDate and FileDownloaded.  DownloadDate will be a DateTime field with the default value being the current date and time.  We’ll adjust our default view for our library to just show these three columns.

 visitorinfo

 Next, we have SuperSoft create a couple of document libraries for storing our EDP files.  We have them first create a Connections library that we’ll use to hold our EDP connection file and we have the create a Providers library that we’ll hold our EDP.  Once these are created we’ll create our connection file and we’ll be using the EDP to connect to the SharePoint Lists web service.

 Setting up our connection file is the key to getting the data that we are looking for.  We create a new XML file and we we’ll configure it with the required data provider connection information for using a Web Service.  In this connection file we will set it up to accept a parameter, in this case we’ll call it %sw%, which will be the actual URL to the file being downloaded and the connection file will also utilize the built-in CorasWorks parameter, CWUserID, which returns us the currently logged in user. 

 In order to setup our connection file, we need to have the Soap envelope and action information from the web service.  The easiest way to do this when working with a .NET based web service, is to browse to the web service using Internet Explorer and get our information.  To find the Lists web service, we type in our current site information and append _vti_bin/Lists.asmx to it… so our URL might look like: http://sp.mysite.com/software/_vti_bin/Lists.asmx, when we browse to this page it will return a list of public methods, we’ll find the one we need, the “UpdateListItems” method and copy the SOAP envelope and Action information.  Once we have the SOAP envelope information in place, then we need to make a small adjustment so that it will correctly call the UpdateListItems method.  More information on this can be found here: http://msdn.microsoft.com/en-us/library/lists.lists.updatelistitems.aspx

 The end result is that our connection file will look like:

<?xml version="1.0" encoding="utf-8" ?>
<Data>
    <Name>InsertVisitorInfo</Name>
    <Default>true</Default>
    <ConnectionType>Web Service</ConnectionType>
    <RedirectTo>%sw%</RedirectTo>
    <Request>%SiteURL%/_vti_bin/Lists.asmx</Request>
    <SOAP>
        <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
            <soap:Body>
                <UpdateListItems xmlns="http://schemas.microsoft.com/sharepoint/soap/">
                    <listName>VisitorInfo</listName>
                    <updates>
                        <Batch OnError="Return">
                            <Method ID="1" Cmd="New">
                                <Field Name="User">%CWUserID%</Field>
                                <Field Name="FileDownloaded">%sw%</Field>
                            </Method>
                        </Batch>
                    </updates>
                </UpdateListItems>
            </soap:Body>
        </soap:Envelope>
    </SOAP>
    <SOAPAction>http://schemas.microsoft.com/sharepoint/soap/UpdateListItems</SOAPAction>
    <UseCurrentUserCredentials>2</UseCurrentUserCredentials>
    <OutputType>text/xml</OutputType>
    <Values>
        <sw />
    </Values>
</Data>

Notice that we use our %sw% parameter in a couple spots in the connection file, first you’ll see it in the <RedirectTo> node, this tells the EDP to redirect it’s web request to the supplied value, in this case it will be the path of the file to be downloaded.  We also use the %sw% parameter to store that URL in our FileDownloaded column in the VisitorInfo custom list we created.

 The hard part has now been created and configured, the next task we need to complete is to create a new Web Part Page (Site Actions > Create) and we’ll name ours VisitorsDP.aspx, we’ll use “Full Page, Vertical” and we’ll save it to our Provider library we created.  Our new web part page will be opened in edit mode, so we’ll click on “Add a Web Part” and select our External Data Provider.  Now we need to setup the EDP, so we click on “Edit” and select “Modify Shared Web Part” and we need to set two properties.  First, under the “Source XML” group, we’ll set our “Source XML File Location” property to point to the connection file we created above:

 <%SiteURL%>/Connections/VisitorSource.xml

 Note: The %SiteURL% parameter is another built-in CorasWorks parameter that will return back the path of the current site that we are in.

 The second property we need to set is under the “Output Properties” group and that is to check the “Output To XML” property.  Once we have these set, we click “OK” and refresh our page… the page should return us information about our call to the web service with some error information that would look something like:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
        <UpdateListItemsResponse xmlns="http://schemas.microsoft.com/sharepoint/soap/">
            <UpdateListItemsResult>
                <Results>
                    <Result ID="1,New">
                      <ErrorCode>0x00000000</ErrorCode>
                      <ID />
                      <z:row ows_ContentTypeId="0x010002C6DFE43EC9F549B89508FE53C52E4C" ows_User="CW\kdager" ows_DownloadDate="2009-04-07 15:30:06" ows_ID="2" ows_ContentType="Item" ows_Modified="2009-04-07 15:30:06" ows_Created="2009-04-07 15:30:06" ows_Author="90;#Kevin Dager" ows_Editor="90;#Kevin Dager" ows_owshiddenversion="1" ows_WorkflowVersion="1" ows__UIVersion="512" ows__UIVersionString="1.0" ows_Attachments="0" ows__ModerationStatus="0" ows_SelectTitle="2" ows_Order="200.000000000000" ows_GUID="{10485444-B8F2-49F8-81C2-52366E3DD1FB}" ows_FileRef="2;#dager/ws/Lists/VisitorInfo/2_.000" ows_FileDirRef="2;#dager/ws/Lists/VisitorInfo" ows_Last_x0020_Modified="2;#2009-04-07 15:30:06" ows_Created_x0020_Date="2;#2009-04-07 15:30:06" ows_FSObjType="2;#0" ows_PermMask="0x7fffffffffffffff" ows_FileLeafRef="2;#2_.000" ows_UniqueId="2;#{FE9CEC14-FC47-44C1-808B-A404EC29707D}" ows_ProgId="2;#" ows_ScopeId="2;#{D6B4CB69-82DC-4807-A211-E241DB00ADD6}" ows__EditMenuTableStart="2_.000" ows__EditMenuTableEnd="2" ows_LinkFilenameNoMenu="2_.000" ows_LinkFilename="2_.000" ows_ServerUrl="/dager/ws/Lists/VisitorInfo/2_.000" ows_EncodedAbsUrl="http://devrapps.corasworks.net/dager/ws/Lists/VisitorInfo/2_.000" ows_BaseName="2_" ows_MetaInfo="2;#" ows__Level="1" ows__IsCurrentVersion="1" xmlns:z="#RowsetSchema" />
                      </Result>
                  </Results>
              </UpdateListItemsResult>
          </UpdateListItemsResponse>
      </soap:Body>
  </soap:Envelope>

 The reason this returns in this fashion is because we setup our EDP to expect a value being passed in, remember our %sw% parameter we configured?

 Everything is now setup for our tracking purposes.  What SuperSoft will do from this point, is on their main software download page, instead of setting the file download URL to the actual file itself, they would set their link to first call the EDP and pass in the URL of the file to download:

http://www.supersoft.com/software/providers/VisitorDP.aspx?sw=http://www.supersoft.com/software/download/superproduct.zip

 As soon as the end user clicks on the link to download the software, the EDP logs the needed info, calls the redirect which will immediately begin the download process, seemless to the end user.  When we check our VisitorInfo list, we’ll see we have user info tracked!

 

 visitorinfo2

Happy Coding!

Redirect A Connection, Feed A Parent and Child

In this scenario, we are utilizing a Workplace Parent Child field, which gives us “dependent dropdowns”, meaning the child values are dependent upon what is selected in the parent dropdown.  With this, we want to retrieve our data from a SQL Server database.  Our data will be driven depending on the SharePoint site that we are wanting information about.  For example, we may have three sites: IT, HR and Accounting, and we want to retrieve a list of tasks in our child dropdown that are based on upon the projects in our parent dropdown, but to start the data rolling, we need to pass the name of the site to our SQL database to get the appropriate projects and tasks.

We accomplish our mission, by first retrieving the Site title utilizing the SharePoint SiteData Web service, then we’ll redirect our connection to take our Site title and pass it in as a parameter to our SQL Server connection.  How do we accomplish this?  We’ll utilize the External Data Provider and the Workplace Parent Child field.  First, let’s set up our Connection file for our EDP.  It will look like this:

<?xml version="1.0" encoding="utf-8" ?>
<CorasWorks>
    <Data>
        <Name>GetSiteTitle</Name>
        <Default>true</Default>
        <ConnectionType>Web Service</ConnectionType>
        <Request>%URLToSearchFor%/_vti_bin/SiteData.asmx</Request>
        <RDConnection>&lt;%SiteURL%&gt;/Providers/SiteDP.aspx?ConnectionName=SelectProjects</RDConnection>
        <RDConnectionPost>
            <PassThrough>
                <Item>
                    <PTType>post</PTType>
                    <PTName>SiteTitle</PTName>
                    <PTPath>data/site/title</PTPath>
                </Item>
            </PassThrough>
        </RDConnectionPost>
        <SOAP>
            <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
                <soap:Body>
                    <GetWeb xmlns="http://schemas.microsoft.com/sharepoint/soap/" />
                </soap:Body>
            </soap:Envelope>
        </SOAP>
        <SOAPAction>http://schemas.microsoft.com/sharepoint/soap/GetWeb</SOAPAction>
        <XslStylesheetLocation>&lt;%SiteURL%&gt;/Providers/SiteTransform.xsl</XslStylesheetLocation>
        <UseCurrentUserCredentials>2</UseCurrentUserCredentials>
        <UserName>%CWUserID%</UserName>
        <Password>%CWPassword%</Password>
        <OutputType>text/xml</OutputType>
        <Values>
            <URLToSearchFor/>
        </Values>
    </Data>
    <Data>
        <Name>SelectProjects</Name>
        <Default>false</Default>
        <ConnectionType>ADO</ConnectionType>
        <ConnectionString>Provider=SQLOLEDB;Data Source=1.2.3.4;Initial Catalog=MyDatabase;Integrated Security=SSPI;</ConnectionString>
        <Query>Select ProjectName From dbo.Projects Where ProjectSite = '%SiteTitle%'</Query>
        <Values>
            <SiteTitle />
        </Values>
        <UseCurrentUserCredentials>1</UseCurrentUserCredentials>
    </Data>
    <Data>
        <Name>SelectTasks</Name>
        <Default>false</Default>
        <ConnectionType>ADO</ConnectionType>
        <ConnectionString>Provider=SQLOLEDB;Data Source=1.2.3.4;Initial Catalog=MyDatabase;Integrated Security=SSPI;</ConnectionString>
        <Query>Select Tasks From dbo.Tasks Where ProjectName = '%ProjectName%'</Query>
        <Values>
            <ProjectName />
        </Values>
    </Data>
</CorasWorks>

Our first connection, GetSiteTitle is our default connection and will execute when the EDP is called.  The connection expects the parameter URLToSearchFor, so you can specify where the site is in your EDP call that you want to point to.  Using the <RDConnection> properties, we specify that we want to redirect our connection back through our EDP and calling our second connection, SelectProjects, which populates our parent dropdown.  Through the <PassThrough> properties, we pass the Site title on to the next connection.  In order to get our Site title XML data in a format for our next connection, we strip out the Site title returned from the web service using our XSLT transformation.  This file looks like:

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:ddw1="http://schemas.microsoft.com/sharepoint/soap/" version="1.0" exclude-result-prefixes="xsl msxsl ddwrt"
    xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" >
    <xsl:output method="xml" omit-xml-declaration="yes" />
    <xsl:template match="/">
        <data><site><title><xsl:value-of select="/soap:Envelope/soap:Body/ddw1:GetWebResponse/ddw1:sWebMetadata/ddw1:Title" /></title></site></data>
    </xsl:template>
</xsl:stylesheet>

Now that we have our Site title retrieved, and redirecting to our SQL Server connection, we need to setup the Workplace Parent Child Field to use our connection.

 We’ll create a new Workplace Parent Child field in our library and configure our “Field Configuration” property to look like this:

<FieldControls>
    <Field>
        <Name>Projects</Name>
        <XMLToURL>&lt;%SiteURL%&gt;/Providers/SiteDP.aspx?URLToSearchFor=http://myportal.mycompany.net/IT</XMLToURL>
        <DisplayValue>:xpath=NewDataSet/Data/ProjectName</DisplayValue>
        <ValueMember>:xpath=NewDataSet/Data/ProjectName</ValueMember>
        <Parent />
        <DefaultText>Select a Project</DefaultText>
        <UpdateFieldName>Project</UpdateFieldName>
    </Field>
    <Field>
        <Name>Tasks</Name>
        <AutoSelectFirstValue>true</AutoSelectFirstValue>
        <XMLToURL>&lt;%SiteURL%&gt;/Providers/SiteDP.aspx?ConnectionName=SelectTasks&amp;ProjectName=%ParentValue%</XMLToURL>
        <DisplayValue>:xpath=NewDataSet/Data/Task</DisplayValue>
        <ValueMember>:xpath=NewDataSet/Data/Task</ValueMember>
        <DefaultText>Select a Task</DefaultText>
        <Parent>Projects</Parent>
    </Field>
</FieldControls>

In the first <Field> node, we define the parent, call our EDP passing in our URLToSearchFor parameter.  Of note, the <UpdateFieldName> node is specifying another column that we created, a single line of text named Project, so that when the values are saved, the value of the selected Project will be saved to that field.  Our second <Field> node is setting up our child dropdown, which calls our EDP, connecting to our third connection and passing in the value of the parent (%ParentValue%).

Now that we have our pieces wired up, when a new item to our list or in the image below, edit an item, we can see that our dependent list boxes are populated based on the site we chose (for the example, IT) and the project and tasks accordingly.

So we have successfully configured our solution to use the SharePoint web service to start our data gathering and populate our dependent dropdowns utilizing the External Data Provider and Workplace Parent Child field of the Data Integration Toolset!

 
Posted by Kevin Dager on 19-Mar-09

Variables – And the External Data Providers that Use Them

Users of the Data Integration Toolset are likely familiar with the “Common Use Variables” – <%SiteURL%>, <%HostName%> and <%SiteSecurityType%> – as these can be used throughout the Toolset web parts, anytime you have a property expecting a URL. These are great because they allow you to build your solutions with essentially relative paths throughout, ensuring portability and re-usability of the components.

However, did you know the External Data Provider (EDP) supports these, along with a dozen or so more variables? Specifically, these variables can be utilized within your EDP connection files and really extend the options you have for not only setting URLs but also passing details into the connection.

Listed here is a table of all the currently supported variables. For those that relate to user information, we’ll use the following details to illustrate.

Sample User Login: EXT\Sample.User

Sample User Name: Sample User

Sample User Email: sample.user@corasworks.net

Name Description (example)

%SiteURL%

The fully qualified URL for the site the EDP is currently on.
(http://www.url.com/sites/SampleSite)

%HostName%

The Hostname of the web application where the EDP currently resides.

(www.url.com)

%SiteSecurityType%

The security protocol of the web application where the EDP currently exists.

(http:// or https://)

%CWUserName%

The username of the currently logged in user

(EXT\Sample.User)

%CWUID%

The ID of the currently logged in user
(14)

%CWUserEmail%

The email address of the currently logged in user

(sample.user@corasworks.net)

%CWDisplayName%

The display name of the currently logged in user

(Sample User)

%CWUserNameND%

The username of the currently logged in user, minus any domain

(Sample.User)

%CWSiteURL%

The current URL of the site where the EDP resides – deprecated, same as %SiteURL%

(http://www.url.com/sites/SampleSite)

%CWSiteTitle%

The site title of the site where the EDP currently resides

(Sample Site)

%ReferringPage%

The URL of the referring page that called the EDP
(http://www.url.com/sites/SampleSite/Report.aspx)

Let us know how you’re leveraging these variables, such as for tracking which users are viewing or adding to your external data. Or perhaps you have ideas for other variables that would help you build solutions – we would love to hear them!

Posted by Steve P. Evangelista on 6-Mar-09

Expanded Field Validation in BDF

The latest release of the Data Integration Toolset provides expanded field validation within the Business Data Form Adapter.

Below you will find the configuration for setting up field validation.  In the configuration below, TestField is being compared to TestField2.  In order to pass the validation rule, TestField must equal TestField2.  If they are not equal, a validation message will appear and the form will not submit.

<Control type="TextBox">
    <ControlName>TestField</ControlName>
    <ControlSize>150</ControlSize>
    <DefaultText>2</DefaultText>
    <ValidationRules>
        <Validation type="fieldcompare">
            <Child>TestField2</Child>
            <Value>equal</Value>
            <ConvertValueTo>number</ConvertValueTo>
        </Validation>
    </ValidationRules>
</Control>
<Control type="TextBox">
    <ControlName>TestField2</ControlName>
    <ControlSize>150</ControlSize>
    <DefaultText>1</DefaultText>
</Control>

In the <ValidationRules> node, you will find the <Validation> node which indicates that a fieldcompare rule is being established.  You have a number of comparison options listed in the table below.

Valid Comparison Value Options
greaterthan
greaterequal
lessthan
lessequal
equal
notequal

The Child node indicates which field the current control is being compared to.  And the Value node indicates what type of compare will occur.

The ConvertValueTo indicates how the fields should be treated when being compared.  The table below lists the valid field conversion options.

Valid ConvertValueTo Options
Number
Date
String

This expanded validation capability has certainly added a lot of value for doing form validation using the Business Data Form Adapter.  If more complex validation needs to take place, you can always write javascript to do other kinds of validation on fields.  For an example on how to implement this technique, please check out this blog post that discusses Date Validation.

Rodney Fickas

Professional Services

Posted by Rodney Fickas on 6-Mar-09

Launch Actions with Style

One of the great many new features of the V10 Workplace Suite is the CSS customizability of the individual components. By default, as in the past, many of the styles used to format the display of the CorasWorks components were inherited from stock SharePoint classes. However, with V10, while we’ve maintained that inheritance, we’ve expanded support for not only inheriting more styles from the site (especially good when you’ve customized your CSS extensively or applied a custom theme) but also allowing you to actually have individual web parts using your own custom CSS.

One of the best instances to take advantage of this customizability is with the Action Launcher. The out-of-the-box styling of the Action Launcher is configured to make it look exactly like the native SharePoint Site Actions menu – much like you see here:

image

Now let’s say you’ve customized your site design; or better yet, you’re using one of the CorasWorks templates which has the sleeker silver-styling. In the case of our template, we’ve modified the Site Actions menu and moved it to the top-right corner, next to the Welcome menu. It’s a great design approach, maximizing real estate and polishing the appearance however the native Action Launcher display wouldn’t exactly look great here:

image

Enter the CSS customization options. Using the new option to allow for custom CSS to be applied, all the way down to the individual web part layer, I can now apply new styles to my Action Launcher to meet my design needs. Out with the old “blue button” styling and in with the new:

image

Here’s the CSS I used to re-style my Action Launcher. Paste this into a CSS file, upload it to your SharePoint and point any of your Action Launchers at it to modify their appearance. Want to apply these changes site or web app wide? No problem, place the style into a local copy of your Core.CSS to affect a whole site or the server-side Core.CSS to affect the entire web app – the scope of the change is up to you!

/* Customized Action Launcher CSS - CorasWorks (2009)*/
.cw-ms-siteactionsmenu {
white-space: nowrap;
font: 8pt verdana;
border: none;
cursor: pointer;
border-collapse: collapse;
}
.cw-ms-siteaction {
border: none;
margin: 1px;
color: #4c4c4c;
vertical-align: top;
background-color: transparent;
}
.cw-ms-siteactionsmenu div div div {
border-width: 1px;
border-color: transparent;
border-style: solid;
}
div.cw-ms-siteactionsmenuhover {
white-space: nowrap;
font: 8pt verdana;
border-width: 1px;
border-color: #6f9dd9 !important;
border-style: solid;
vertical-align: top;
background-color: #ffbb47;
background-image: url(/_layouts/images/menubuttonhover.gif);
padding-left: 3px;
}
.cw-ms-siteactionsmenuhover a, .ms-siteactionsmenuhover a:hover {
color: #000000;
text-decoration: none;
}
.cw-ms-siteactionmenu, .cw-ms-siteactionmenu a {
font: 8pt verdana;
color: black;
}
.cw-ms-siteactionsmenu div div div A:link, .cw-ms-siteactionsmenu div div div A:visited {
color: #000000;
font-weight: normal;
}
.cw-ms-siteactionsmenu div div div.cw-ms-siteactionsmenuhover a:hover {
color: #000000;
}

Of course, the customizations are not limited to what I’ve done here. Feel free to further customize it yourself – and check back soon for additional posts on customizing other V10 web parts.

Posted by Steve P. Evangelista on 2-Mar-09

XML: How Do I Group Thee?

Not necessarily specific to any one CW web part, and definitely not new but this little XSLT method will help you group flat XML data into more meaningful related groups.  The method is commonly referred to as Muenchian Grouping and was proposed back in 2000 by Steve Muench for working in the 1.0 version of XSLT.  It involves the use of the key() and the generate-id() methods. Let’s take a look at our source data first:

<employees>
<employee>
<name>John Doe</name>
<department>IT</department>
<email>john@mywork.com</email>
</employee>
<employee>
<name>Bob Smith</name>
<department>IT</department>
<email>bob@mywork.com</email>
</employee>
<employee>
<name>Barb Jones</name>
<department>HR</department>
<email>barb@mywork.com</email>
</employee>
<employee>
<name>Sally Weber</name>
<department>Marketing</department>
<email>sally@mywork.com</email>
</employee>
<employee>
<name>Steve Walker</name>
<department>HR</department>
<email>steve@mywork.com</email>
</employee>
</employees>

Our goal here is to group our employees by their department, so that will be our key.  To do this in our XSLT file, we create a new key:

<xsl:key name=”dept” match=”employee” use”=”department” />

So what is this key telling us?  The “name” is how we’ll refer to this key in our XSLT code, the “match” tells us what node group to look for a match and the “use” is the actual node that we want to group on.

Next we need to create a unique identifier for the node we want to group together and we accomplish this by using the generate-id() method.  The generate-id() method returns a string value that uniquely identifies the specified node. Our syntax for that looks like:

generate-id(key(‘dept’, department))[1]

Notice that we are using our key() method to define our specific node.  The key() method returns a node-set based on the <xsl:key> element.  Coupling these two methods together gives us uniqueness with our departments.  So when we put all this together in our stylesheet, it would look like this:

<xsl:key name=”dept” match=”employee” use=”department” />
<xsl:template match =”employees”>
<xsl:for-each select =”employee[generate-id()=generate-id(key('dept', department)[1])]”>
<ul>
<li style=”list-style-type:none”>
<b>
<xsl:value-of select=”department”/>
</b>
</li>
<ul>
<xsl:for-each select=”key(’dept’, department)”>
<li style=”list-style-type:none”>
<xsl:value-of select=”name”/> - <xsl:value-of select =”email”/>
</li>
</xsl:for-each>
</ul>
</ul>
</xsl:for-each>
</xsl:template>

Two items to point out, first is the first <xsl:for-each> statement.  This is selecting the unique identifier for our group, in this case it is returning only a single instance of our department value.  The second <xsl:for-each> is looping through the remaining data based on the key that we specified (in this case, department).

In the end, our transformed data based on our stylesheet above looks like:

IT

John Doe - john@mywork.com

Bob Smith - bob@mywork.com

HR

Barb Jones - barb@mywork.com

Steve Walker - steve@mywork.com

Marketing

Sally Weber - sally@mywork.com

Though not a new technique for grouping, it is often overlooked as a valuable grouping solution for flat XML data.  Happy coding!

Date Control Validation

A scenario came up when saving a masked date control in the Business Data Form adapter back to SharePoint through Web Services, if a value was not specified the masking characters will be sent into the SharePoint web service. The SharePoint Web Services expect the date to be in a format of yyyy-MM-dd, however in the case of the date control, without the date control being a required field, the date would be passed into the Web Services in an invalid format (similar to ‘____-__-__’) and cause an error because it is not a valid date.

With the CorasWorks controls, the ability to alter the control’s properties is available by using the function GetClientIDValues(a, b); Where a is the title of the Business Data Form Adapter web part and b is the name of the CorasWorks control. So using this function the javascript: document.getElementById(GetClientIDValues(a, b)) you can gain access to all the control’s properties.

So now in our BDF source, we can create a javascript function to validate the date control to make sure that if the date isn’t required, that we format the value being passed to allow the save to be successful. To make this work, we must specify the CheckValue() method in the “Extra Methods” property of the BDF. This entry would look like: var Page_IsValid = CheckValue(); This instructs the BDF to call the CheckValue() function when we submit the form.

function CheckValue()
{
    if (document.getElementById(GetClientIDValues(a, b)).value == "____-__-__")
    {
        document.getElementById(GetClientIDValues(a, b)).value = "";
        Page_IsValid = true;
        return true;
    }
}

What the Hex(c0de)?!

Working in SharePoint, you may have noticed the encoding of non-alphanumeric characters you add to your column names. This is often referred to as a “SharePoint Escaped Value” for those characters that need transformation for use in a column name.

For example, consider the column name “% Complete”, common in Task lists and related-type content. If you were to say view the settings for that column, or sort/filter on it, you would see that percent sign encoded as “_x0025_” in the address bar.

If you’re working with the Data Integration Toolset, these character encodings become even more visible. When using a SharePoint Data Provider, these encoded characters can be seen in the node names of your output. The Toolset displays are intelligent enough to decode these back to the proper characters when showing the column names, but what if you want to transform or otherwise modify your Provider data outputs?

Provided here is a listing of the most common SharePoint Encoded Values for non-alphanumeric values. If there are any others you wish to know, the format for these values is always a concatenation of:
_x00 + the 2-character Hex Code + + “_

Standard Character
SharePoint Encoded Value
Space
_x0020_
!
_x0021_
_x0022_
#
_x0023_
$
_x0024_
%
_x0025_
&
_x0026_
_x0027_
(
_x0028_
)
_x0029_
*
_x002A_
+
_x002B_
,
_x002C_
-
_x002D_
.
_x002E_
/
_x002F_
?
_x003F_
@
_x0040_
~
_x007E_

Posted by Steve P. Evangelista on 17-Feb-09

Page Utility Pop-Up Window

I recently posted about how many times in Toolset implementations there is a requirement to pop-up a window. Often times these pop-ups come from a grid display so you can display a Business Data Form Adapter for editing information. And at other times to display a new form for creating new items.

In my last post (you can find it here), I outlined how to use a third-party CSS pop-up window. There are a number of script files needed to make that work and managing those script file references can become cumbersome.

Since that blog was posted, CorasWorks released version 1.3 of the Toolset. In this release, there are many new capabilities. And I’m sure we will be blogging about them in the near future.

But as it pertains to this pop-up requirement, we are now able to handle very nice looking pop-ups using the updated Page Utility web part.

The implementation is straightforward and I walk you through the steps below.

1) Add a Page Utility web part to the page where you would like to have a pop-up

2) You will want to hide this web part

3) Configure the Windows Setup property under Page Loader Setup Properties (I’ve shown a simple configuration below but there are many different configuration options.)

<popup>

<window>

<ID>Admin</ID>

<Width>1024</Width>

<Height>300</Height>

<VisibleOnPageLoad>false</VisibleOnPageLoad>

<VisibleStatusBar>false</VisibleStatusBar>

<Modal>true</Modal>

<Title>Add New Account</Title>

<Skin>WebBlue</Skin>

<MinimizeZoneID>holdminimizehere</MinimizeZoneID>

<KeepInScreenBounds>true</KeepInScreenBounds>

</window>

</popup>

4) Note what you put in the ID node it will be needed when invoking the window from your display

In the screenshot below, we have a Content Editor Web Part (CEWP) that is providing a way for the user to open a new window for adding new accounts. The HTML for that CEWP is provided below.

5) Drop a CEWP on your page and include the following HTML

<table height="25" cellpadding="2" width="100%" class="ms-toolbar" border="0">
<tr>
<td class="ms-toolbar">
<a style="text-decoration: none" href="#" onclick="CWChangeWindowLocationAdmin(
'AccountAdmin.aspx');">
<img align="absmiddle" border="0" alt="Add New Account" src="/_layouts/images/newitem.gif"
style="border-width:0px;" width="16" height="16"/>
</a>
<a href="#" onclick="CWChangeWindowLocationAdmin('AccountAdmin.aspx');">Add New Account</a>
</td>
</tr>
</table>

Notice the “onclick” script for the icon and the Add New Account text link. The CWChangeWindowLocation script is built-in script that will open a new window. By appending your ID (Admin) from the Window Setup property, it references the right setup for which window to open. The AccountAdmin.aspx will be loaded in the pop-up.

The result when the user clicks on the link in the CEWP is this very professional looking pop-up. No managing of scripts is necessary.

Rodney Fickas

Professional Services