Thursday, May 28, 2009

SharePoint Walktrough - Displaying List Items with their attachments Part 2/2

image

Recap

The SharePoint Out Of the Box List View Web Part is quite powerful, yet it isn’t able to display the attachments of a List Item. It can show if an item has any attachments (with a nice paper-clip icon) but you won’t be able to download these attachments directly.

I presented in Part 1 of this walkthrough how to create a DataView part consuming the Lists.asmx Web Service in SharePoint Designer to display the elements of a list. We are now going to display the attachments of these list elements.

To retrieve the attachments URLs we need to add an option to the Web Service Soap query, the “IncludeAttachmentUrls” option (details of the query options is available at http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spquery_properties.aspx). We could just open the datasource and change the QueryOptions parameter, that is if the SharePoint WSDL files were correctly defined… As it is not the case and SharePoint Designer itself isn’t perfect, you won’t be able to do that (see http://blogs.msdn.com/sharepointdesigner/archive/2008/06/20/data-source-issues-and-workarounds.aspx for reference).

But there is a workaround!

4 – Modify the DataSource QueryOptions parameters

imageFirst you need to have access to the page source, so let’s switch SharePoint Designer to Split or Code view :

In the code, search for the line starting with: “SharePoint:SoapDataSource”. On the same line you shall see something like “ </listName></GetListItems></soap:Body>”.

image

As you can see the parameters we defined in the Data Source Gui are found here and guess what, we can modify these parameters from here! So let’s add the parameter requesting the attachments url :

<queryOptions><QueryOptions><IncludeAttachmentUrls>TRUE</IncludeAttachmentUrls></QueryOptions></queryOptions>

So that the end of the soap request body looks like :

</listName><queryOptions><QueryOptions><IncludeAttachmentUrls>TRUE</IncludeAttachmentUrls></QueryOptions></queryOptions></GetListItems></soap:Body>

Save the page and check the result in the design view :

image

The ows_attachments field now contains the attachments URLs or 0 for list items with no attachments using the following format :

;#http://moss/Lists/MyList/Attachments/1/Piece.txt;#http://moss/Lists/MyList/Attachments/1/Piece2.txt;#

We still need to make some nice links out of this field. Time to put on your XSL designer gear.

5 – Modify the XSL Presentation

We first create a XSL template to transform the string given by the Web Service into a nice series of picture. You might use my sample below, paste this piece of code in the XSL part of the aspx page you are editing in SharePoint Designer (maybe just before the node “</xsl:stylesheet>”).

<xsl:template name="SplitAttachments">
<xsl:param name="str"/>
<xsl:choose>
<xsl:when test="contains($str,';#')">
<xsl:variable name="attachmentUrl" select="substring-before($str,';#')"/>
<xsl:if test="string-length($attachmentUrl) != 0">
<a href="{$attachmentUrl}"><img style="border:0px" src="/_layouts/images/attach.gif" alt='Open'/></a>
</xsl:if>
<xsl:call-template name="SplitAttachments">
<xsl:with-param name="str" select="substring-after($str,';#')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

This XSL template uses a recursive template to parse the attachments and generate corresponding links and pictures.

Now replace

<xsl:value-of select="@ows_Attachments"/>

By

<xsl:call-template name="SplitAttachments">
<xsl:with-param name="str" select="@ows_Attachments" />
</xsl:call-template>

And you should see this :

image

The paper clip icon you see are actually pointing to the attachments.

6 – Pimp up my Data View

At this point our Data View Web Part works ok but isn't as nice as you might want it to be.

As an example you might want to change these paper clips icons with icons representing the file type. The good news is that the XSL extension provided by WSS (default prefix used by SharePoint Designer is DDWRT) can handle this through a MapToIcon template. If you give a file extension to this template, it will return the icon filename corresponding to this filetype.

Using the substring-after method to get the filetype we can imagine replacing :

/_layouts/images/attach.gif

By

/_layouts/images/{ddwrt:MapToIcon('',substring-after($attachmentUrl,'.'))}

imageNow save the file and check the result :

It still isn't perfect but from this point on things will be much easier to improve if you know your HTML and a bit of XSL.

7 – Conclusion

That’s it, you have got a functional Web Part to display your List.

Using this method you can specify every parameters of the GetListItems method, as an example the ViewName parameter can prove usefull to filter items. If you need to test your parameter I would suggest you to try soapui.org.

You can of course export this web part to put it another page, even on another page of another SharePoint farm (in this case, you will have to use the basic authentication mode unless kerberos authentication can do the trick). If you want to display another list you can just export the WebPart and edit the file before reimporting it. This might prove very useful to put this web part on a publication page as these pages can’t be edited using SharePoint Designer.

If you need more details you can contact me by leaving a comment or using the "IM Me" box on the right of this page.

27 comments:

PACMAN said...

Nice job! I once had try to reach the same goal but didn't have time to make it happen.
We just need to hope that Kerberos make the user authentification working....

Chris Douglas said...

I am trying to use the lists.asmx web service to return a project tasks list and display my tasks in a DataViewWebPart, but the status field is not returned. How do I get the status field for a task list?

Joe said...

Chris,

I just tried retrieving Tasks status from a Task list and everything seems to be working fine. Try only specifying the ListName with no other options. If the status field still isn't returned, make sure it is part of the default view.

If it doesn't work try specifying the viewFields following : http://msdn.microsoft.com/en-us/library/lists.lists.getlistitems.aspx

Joe

Chris Douglas said...

Joe,

Now I really don't understand. I tried connecting to a SharePoint site in a different domain to test this capability, and I had to hard-code the user name and password in the Login tab on the Properties window. Voila, the Status field _AND_ the Assigned To field (didn't notice that was missing too) magically appear! Doesn't work using Windows Authentication, and it doesn't work on my original site when I hard code my account info. Curiouser and curiouser!

Joe said...

Curious indeed, I got to admit that authentication when using the SP Web Services in SPD is a pain.

What are the differences between your two env ?

part 1 of this walkthrough (http://blog.jonathanroussel.com/2009/05/sharepoint-walktrouh-displaying-list.html), section "Changing the Web Service authentication:" might help you dealing with these problem.

Joe

Unknown said...

Wonderful - but I am trying to get the "Created By" info out - when attaching to a list no probs, but it doesn't appear to be in the dataset returned when connecting as a webservice

Chris M said...

Correction to previous post - the "created by" is there, but called ows_author - but it is just the text of the name - any ideas how this can be requested to include a RTF version that includes the link to the users page (like we'd get if we connect direct to the list)

I really appreciate this blog as it enabled me to re-create our old "for sale" board" which had displayed pictures along side the entries with only a slight tweak to the code.

Joe said...

Thank's for the input Eserim.
I'm very glad this post was helpfull to you.

Delpot said...

awesome! just what i needed.

Chris M said...

HELP - all was working fine, I had a filter based on @ows_Expires - then I made changes to one of the normal views, and added another view - all through the browser interface.
Next thing I know @ows_Expires isn't being returned in the service based recordset! Any ideas?

Eserim

Anonymous said...

this is a really gr8 post
loved it :)

Anonymous said...

Hi Jonathan,
After trying the below
/_layouts/images/{ddwrt:MapToIcon('',substring-after($attachmentUrl,'.'))}

it doesn't seem to work.
I get no images it all shows the generic image

Unknown said...

Hi
how cam display name of the files beside of its icons?

Suryakant Lokhande - O365 & AZURE Expert ,PRINCE2® & Agile Practitioner said...

Can please tell how to show the attachment icon when list is populated through code.I mean I written dll for it.My all attachment by default get stored at list root folder and when I seen it through SPD those are seen in ITEM section not in attachment section. Please help if any idea

Joe said...

Suryakant,

Are you able to get the URLs of the Attachments yet ?
If not, try looking for the field/property you need with SharePoint Manager (for SharePoint 2007 and 2010) at http://spm.codeplex.com/.

As for retrieving the file type icon server-side, there is a public static method named MapToIcon (http://msdn.microsoft.com/en-us/library/ms468490.aspx).

Anonymous said...

Hi,
I've a list with couple folders inside. How do you ask the xsl to display all the items within the folders?

Joe said...

Anonymous,

The XSL display only what the web service sends back. To display the content of subfolder you need to specify the parameter "query" in the web service request.

This parameter is a CAML query so I would recommend you to use the U2U CAML Query Builder app to edit it.
This tool will help you generate the CAML you need to display what's in subfolders.

Joe

Unknown said...

If I have to show File name instead of Attachments icon, how would I go about doing this? Thanks.

Charles Babcock said...

Joe,

Your code almost works. As it is, it only displays the generic file icon.

This is what you have:
/_layouts/images/{ddwrt:MapToIcon('',substring-after($attachmentUrl,'.'))}

Here is the fix:
src="/_layouts/images/{ddwrt:MapToIcon('', ddwrt:GetFileExtension(string(substring-after($attachmentUrl,'.'))))}"

I also recommend putting in some space between the icons. Using hspace="5" right before src= will put 5 pixels of white space on either side of the icon. Otherwise, the icons are right next to each other.

Also, putting something friendly in the Alt tag is good such as "Click to Open" instead of just Open.

Joe said...

Hi Charles,

Thanks for the input

I am surprised as the GetFileExtension method does the same as the substring-after("",".") I recommended. That is it gets the part of the string after the dot. Here is the official documentation on the MSDN (http://msdn.microsoft.com/en-us/library/aa505323.aspx#officesharepointddwrt_getfileextension)

Anyway, using the official method would indeed be better but I would remove the substring-after then :


src="/_layouts/images/{ddwrt:MapToIcon('', ddwrt:GetFileExtension(string($attachmentUrl)))}"

Anonymous said...

This was a really great post. Thank you.

Anonymous said...

Thanks a lot. Its very helpful.

MoD said...

Joe

Great blog. The directions are great but SharePoint:SoapDataSource”. does not appear and cant find it. The line <SharePoint:SPDataSource runat="server" DataSourceMode="List".
How do i proceed.

Anonymous said...

I need to actually create a column called attachment and allow user to upload a document. Is there a way to do this in a list?
Thanks,
Ninel

Unknown said...

Very Nice Post.. It helped me a lot.. Can you tell me how to display the latest attachment i.e., only one latest attachment.

Thanks in Advance

Anonymous said...

Is it possible to click the attachment to automatically be set to edit mode?

Gokul said...

IncludeAttachmentUrls does not seem to work with web service option from the client machine. It always gives ows_Attachments="1" if it has attachments not the url. Can you help?