AJAX SharePoint picture gallery using Highslide

Do you like Highslide? It’s a pretty neat JavaScript library that allows you to create AJAX thumbnail viewers and pop-up image galleries. It’s a really easy way to add some Web 2.0 flair to your site instead of displaying boring thumbnails.

Now, SharePoint has an embedded “This week in pictures” Web Part but it’s pretty boring. There are a lot of Silverlight options out there, but I think Highslide is much simpler to implement… You don’t need to install any code on the server, you can do everything using SharePoint Designer!

The trick is of course to use the Swiss Army knife of SharePoint Web Parts: the Data View! The idea is to point a Data View to a standard SharePoint picture library, and then tweak the XSLT in order to display the markup for Highslide.

First, you need to download Highslide and install it on your SharePoint. Just use SharePoint Designer to drop the “highslide” directory in any document library. In this example, I used my My Site as a sandbox, so I copied Highslide to the “My Pages” library.

image

Then, you need to reference the library in your Master Page or your custom ASPX page. If you are working in a custom ASPX page, insert the code below into the “PlaceHolderAdditionalPageHead” placeholder. If you are working in a Master Page, just insert the code in the page header.

<script type="text/javascript" src="/sites/tconte/My%20Pages/highslide/highslide-with-gallery.js"></script>
<link rel="stylesheet" type="text/css" href="/sites/tconte/My%20Pages/highslide/highslide.css" />

Then, you will add some JavaScript configuration code. You can find many examples on the Highslide site, but here is what I use. Of course, make sure to change graphicsDir to point to the location where you installed Highslide!

<script type="text/javascript">
    hs.graphicsDir = '/sites/tconte/My%20Pages/highslide/graphics/';
    hs.align = 'center';
    hs.transitions = ['expand', 'crossfade'];
    hs.outlineType = 'rounded-white';
    hs.fadeInOut = true;
    hs.numberPosition = 'caption';
    hs.dimmingOpacity = 0.75;
 
    // Add the controlbar
    if (hs.addSlideshow) hs.addSlideshow({
        //slideshowGroup: 'group1',
        interval: 5000,
        repeat: false,
        useControls: true,
        fixedControls: 'fit',
        overlayOptions: {
            opacity: .75,
            position: 'bottom center',
            hideOnMouseOut: true
        }
    });
</script>

Next, you need to create the Data View Web Part. You will create it in SharePoint Designer: create an empty ASPX page, go to Data View –> Insert Data View, and a new Task Pane will open on the right hand side. It should show all the libraries in your SharePoint site. Select an image library, and select Show Data.

image

In the Data Source Details pane that opens, you don’t need to do much because we will write most of the XSLT by hand anyway! Just select one field, like ID, and then do Insert Select Fields as… Multiple Item View. This will insert the basic control and parameters we need to then tweak the Data View by hand.

image

Now, switch directly to Code view! You can have a look at the generated XSLT, it’s pretty simple. All we need to do is generate some really simple HTML that references the magic Highslide classes. Here’s an example:

<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" 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:variable name="dvt_1_automode">0</xsl:variable>
    <xsl:template match="/">
        <xsl:call-template name="dvt_1"/>
    </xsl:template>
    <xsl:template name="dvt_1">
        <xsl:call-template name="dvt_1.body">
            <xsl:with-param name="Rows" select="/dsQueryResponse/Rows/Row[1]" />
        </xsl:call-template>
        <div class="hidden-container">
        <xsl:call-template name="dvt_1.body">
            <xsl:with-param name="Rows" select="/dsQueryResponse/Rows/Row[position()&gt;1]" />
        </xsl:call-template>
        </div>
    </xsl:template>
    <xsl:template name="dvt_1.body">
        <xsl:param name="Rows" />
        <xsl:for-each select="$Rows">
            <xsl:call-template name="dvt_1.rowview" />
        </xsl:for-each>
    </xsl:template>
    <xsl:template name="dvt_1.rowview">
        <a href="{@FileRef}" class="highslide" onclick="return hs.expand(this)"><img border="0" src="{concat('/',@FileDirRef,'/_t/',substring(@LinkFilenameNoMenu,1,string-length(@LinkFilenameNoMenu)-4),'_',@FileType,'.jpg')}" alt="{@NameOrTitle}" /></a>
        <div class="highslide-caption"><xsl:value-of select="@NameOrTitle"/></div>
    </xsl:template>
</xsl:stylesheet>

Here are a few clues as to what this XSLT does:

  • It just displays thumbnails of all the images in the image library
  • The thumbnails are taken from the SharePoint image gallery, using the magic formula:
    • concat('/',@FileDirRef,'/_t/',substring(@LinkFilenameNoMenu,1,string-length(@LinkFilenameNoMenu)-4),'_',@FileType,'.jpg')
    • (XSLT is so obfuscated, it reminds me of Perl regular expressions!)
  • The first picture is treated separately and becomes the thumbnail for the gallery; the following pictures are enclosed in a specific div that is handled by Highslide.

This should get your mostly started! You may need to perform additional configuration on the Data View, such as filterting or sorting stuff so that you get exactly the pictures you want.

And of course the final result:

image

Posted 01 April 09 02:46 by tconte | 0 Comments   
Filed under
Import your WordPress data into Oxite

Have you tried Oxite yet? Like what you see? Maybe you have a WordPress blog you would like to migrate to Oxite? I have written a very bare-bones import process that will allow you to import your WordPress data into Oxite. Right now I only handle Posts, but I don’t import categories yet; I’ll try to improve the process in the future.

Before using the tool, you must export your data from WordPress. In the Admin interface, go to Manage and then Export. This will let you generate an XML file containing all your posts and their categories.

You will then use this file to import the data into Oxite.

To install the WordPress import page: download the ZIP file below, and add the files into the OxiteSite project. This will add an ImportWP.aspx page. Run Oxite and load this page. It will present you with an Upload control you can use to send your WordPress XML export.

If you look at the code you will see it is very simple! The main method to create a Post in Oxite is as follows:

   1: protected void Create_Post(string title, string bodyShort, string body, string slug)
   2: {
   3:     IPost post = PostRepository.CreatePost();
   4:     
   5:     post.Title = title;
   6:     post.BodyShort = bodyShort;
   7:     post.Body = body;
   8:     post.Slug = slug;
   9:  
  10:     post.CreatorUserID = importUser.ID;
  11:     post.State = (byte)EntityState.Normal;
  12:     
  13:     post.Published = DateTime.Now.ToUniversalTime();
  14:  
  15:     PostRepository.AddPost(post, null);
  16:     PostRepository.SubmitChanges();
  17:  
  18:     AreaRepository.AddPostToArea(post.ID, blogArea.ID);
  19:     AreaRepository.SubmitChanges();
  20: }

One thing you should probably change is the IUser to use to import; by default I will use “Admin”.

Have fun!

[update] After discussing with the Oxite team, I realized I didn't handle properly the Body and BodyShort fields. I will post an update later on with details and a new version of the migration tool!

Posted 12 December 08 02:56 by tconte | 1 Comments   
Filed under
Porting the Silverlight Blueprints for SharePoint to Silverlight 2 Beta2

The recently released Beta2 of Silverlight 2 has unfortunately broken the Silverlight Blueprints for SharePoint. The team has announced on their CodePlex site that they will release a new version for Beta2, but in the meantime, here's a summary of what I changed to make the Blueprints run on Beta2.

Yup, running on Beta2

First, a caveat: I only ported the MediaViewer control, a.k.a. the "Picture Viewer Sample" because that's what I needed. Other samples may require more work!

The main problem I had when I upgraded to Beta2 is the change to HttpWebRequest behavior: its delegates are now called on a background thread, which means you have to change the way it is used in the current Blueprints code (which was written for Beta1, where HttpWebRequest delegates were called on the main UI thread).

Thanks to scorb's great post on Silverlight HTTP networking, here's what I changed:

  1. Moved the call to request.BeginGetResponse() from BuildPreviewBar() to the end of RequestCallback(), so that it is called on the background thread
  2. Hacked the ResponseCallback() method so that it calls back to the main UI thread to set up the thumbnails etc. (using System.Threading.SynchronizationContext)

And that's about it!

Attached to this post: the modified source code for Page.xaml.cs in SL.XAML.MediaViewer.

Posted 16 June 08 05:22 by tconte | 1 Comments   
Filed under
Attachment(s): Page.xaml.cs
Customizing Data Views using ASP.NET controls

I recently realized that the SharePoint Data View Web Part framework provided a means to expose standard ASP.NET data sources. This means that anything that can be exposed using a Data View Web Part can also be utilized in an ASP.NET data bound control: Lists, Document Libraries, Databases, RSS feeds, Web Services, etc. This is pretty powerful! In the following screencast, I will show you how you can create a filtered view of a Document Library; the filter is applied using a standard ASP.NET drop-down list, which is bound to a SharePoint Data Source (in this case, exposing a List).

There is no particular trick involved. All you need to do is go to one of your Data View Data Sources, and select the "Insert Data Source" option. You can then insert any ASP.NET control and bind it to this data source. You can use all of the usual properties, typically in this example, I will set the drop-down list's AutoPostBack property to true, so that the page is automatically refreshed when I select one of the items.

Another key point is of course how to pass the selected value on to a Data View: you cannot use Web Part connections, since the source control is not a Web Part. But the Data View allows you to define custom input parameters, and the source of such a parameter can be a Control, which is how we connect our drop-down list to the Data View filter.

This technique allows you to build seriously complex interfaces without a single line of code!

Here's the embedded Flash version of the screencast. You can also download the full WMV version (27 MB).

Posted 29 April 08 08:41 by tconte | 1 Comments   
ASP.NET MVC Framework

I am fond of MVC. I have done my fair share of Java programming for the Web, and this pattern is nearly ubiquitous in Java-based Web frameworks; I have used Struts quite a bit, and I used it as the basis for the 2004 Olympic Games Web site. Spring, WebWork, are other Java MVC frameworks. I find it fascinating that such an old pattern, dating back to the Xerox Smalltalk era (1979, says WikiPedia), is so well suited to developing applications for the Web.

Microsoft has followed a very different path with its ASP.NET Web Forms framework. Their goal was not to select and utilize a pattern that would match the actual implementation of the Web, with its HTTP/HTML duality that closely matches the Controller and View layers... The goal was to abstract this implementation completely, and reproduce a programming model that would match their most popular programming environment, Visual Basic.

In this sense, ASP.NET Web Forms are simply amazing. You can forget completely about HTML forms and HTTP query parameters, hiding input fields to maintain state, etc. Everything is taken care of for you. You can start programming for the Web exactly as you did in VB for Windows applications. It's very clever.

However, I have always been a little bit uneasy around ASP.NET Web Forms compared to an MVC environment, probably because I don't have a VB background, but rather a pure Web past, i.e. I started writing CGI scripts for NCSA Server way before Apache even existed, and I think my first CGI scripts were written in Unix shell scripts, before I discovered Perl.

It seems I'm not the only one to find some shortcomings to ASP.NET Web Forms, and MVP Rick Strahl describes his view in detail in this blog entry: What's Ailing ASP.NET Web Forms - Rick Strahl's Web Log.

This is why I am very interested by the new ASP.NET MVC Framework that Scott Guthrie announced in October. I think it looks like a fantastic tool to build complex Web sites, and it will allow ASP.NET developers to use a very mature and well-known architectural pattern for their Web developments. I will try to get my hands on the public preview as soon as it's available, hopefully before the end of the year.

(cross-posted to my personal blog)

Posted 03 December 07 02:39 by tconte | 3 Comments   
Filed under
.Net Adventures : 10 awesome Windows Live Writer plugins for developers and bloggers

More cool plug-ins for Windows Live Writer! Blog & prettify your code!

.Net Adventures : 10 awesome Windows Live Writer plugins for developers and bloggers

Live Search Web Part, Part 2

I realized that it may not be immediately obvious to everybody how you can customize the MOSS Search Center to add the Live Search Web Part I blogged about. It is really very simple.

The Theory: all you need to do is to create new "Live" tabs on the Search Center (one for Search and one for Results), plus the corresponding "Search" and "Results" pages (all from the Web interface). Don't forget to modify the Search Boxes to point to the corresponding new pages. Then, you just import the attached Web Part on to your Results pages, and it will automatically use the current query and display the results in the Search Center.

The Practice: here's a quick screencast that shows how to do it: configuring the Live Search Web Part on screencast.com!

Posted 02 October 07 12:48 by tconte | 1 Comments   
Filed under
Live Search Web Part

I have pre-configured a Web Part for MOSS that you can just drop on to a Search Center results page. It will run the current query on Live Search and display the results. It is built using SharePoint Designer, the Data Form Web Part, and the Live Search SOAP API. It automatically retrieves the current search keywords from the Query String, and you can also edit a couple of static parameters in the .webpart file.

image

Nota bene: the Web Part is contained in the MSN_Search_Service.webpart file attached to this post: 8 KB only, and no code required! 

Posted 02 October 07 12:14 by tconte | 4 Comments   
Filed under
Attachment(s): MSN_Search_Service.webpart
Blogging tools

In order to write the previous post, I used two tools that make the work much easier:

Windows Live Writer is a cool tool from the Live team to help you write blog posts. It automatically detects your Blog APIs (like MetaWeblog) as well as your style sheets, and allows you to write your posts in a real WYSIWYG environment. Much better than all the Web-based blog posting interfaces! Download it via the brand new Windows Live unified installer!

c# code format is a nice online tool that will format and color-code your C# / VB / *ML source code for posting on the Web. This is how I formatted the code snippets in my previous post. Nice!

Posted 14 September 07 01:50 by tconte | 1 Comments   
Filed under
Batch loading InfoPath Forms in SharePoint 2007

I have recently been asked to batch load a bunch of InfoPath Forms into a SharePoint Server for access via Forms Server. The customer had a database extract, in the form of a CSV file, and they wanted to convert this data into pre-filled forms, and then load the forms into SharePoint Server. They also wanted to apply some specific ACLs on the forms, because they contained some confidential content.

I looked a little bit around for a ready-made solution, but couldn't find an easy way to use CSV data to pre-generate InfoPath Forms & upload them to SharePoint, so I decided to write a little C# program to solve the problem.

Basically, I divided the problem in 3 chunks:

  1. Convert the CSV file into generic XML
  2. Convert the generic XML into proper InfoPath XML using an XSLT
  3. Upload the generated XML file into SharePoint and modify the ACLs programmatically

1. Converting the CSV into XML

I could probably just use Excel's XML capabilities to do this, but I wanted to automate all the steps in a single program. After digging a bit around the Web, I found this simple solution: XmlCsvReader on MSDN (and a slightly newer version on CodePlex). It's an implementation of an XmlReader that reads CSV input and generates an XML stream. It's really nifty and allows you to convert from CSV to XML very simply:

XmlDocument doc = new XmlDocument();
XmlCsvReader reader = new XmlCsvReader(new Uri("file:///C:/temp/StatusReportData.csv"), Encoding.UTF8, doc.NameTable);
reader.FirstRowHasColumnNames = true;
doc.Load(reader);

I started with this data:

Date,Project,Prepared By,Manager,E-Mail,Department
1-1-2007,1,Thomas Conté,Harry Cover,tconte@microsoft.com,Finance
2-1-2007,2,Bernard Barnier,Harry Cover,bbarnier@microsoft.com,Finance
3-1-2007,3,Charles Duchemin,Harry Cover,cduchemin@microsoft.com,Finance
4-1-2007,1,Ludovic Cruchot,Harry Cover,lcruchot@microsoft.com,HR
5-1-2007,2,Claude Ratinier,Harry Cover,cratinier@microsoft.com,HR
6-1-2007,3,Jean Durand,Paul Dugenou,jdurand@microsoft.com,HR
7-1-2007,1,Paul Martin,Paul Dugenou,pmartin@microsoft.com,HR
8-1-2007,2,Gaston Lagaffe,Paul Dugenou,glagaffe@microsoft.com,IT
9-1-2007,3,Paul Marcel,Paul Dugenou,pmarcel@microsoft.com,IT
10-1-2007,1,Marcel Paul,Paul Dugenou,mpaul@microsoft.com,IT

And XmlCsvReader generates an XML document that looks like this:

<root>
  <row>
    <Date>1-1-2007</Date>
    <Project>1</Project>
    <Prepared_x0020_By>Thomas Conté</Prepared_x0020_By>
    <Manager>Harry Cover</Manager>
    <E-Mail>tconte@microsoft.com</E-Mail>
    <Department>Finance</Department>
  </row>
  <row>
    <Date>2-1-2007</Date>
    <Project>2</Project>
    <Prepared_x0020_By>Bernard Barnier</Prepared_x0020_By>
    <Manager>Harry Cover</Manager>
    <E-Mail>bbarnier@microsoft.com</E-Mail>
    <Department>Finance</Department>
  </row>
</root>

The complete document contains one row element for each line in the original CSV file. Now I need to break up this document in chunks, and convert each row element into a properly formatted InfoPath XML document.

2. Converting the XML into an InfoPath document

An InfoPath document, or form, is just a regular XML file, with a couple of XML Processing Instructions that indicate that the XML file is indeed an InfoPath form. These two expressions, that must be present at the top of the XML file, are:

<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2">

and the mso-infoPathSolution instruction that indicates, among other things, the location of the for template.

Now, our base data being XML and the target InfoPath document being XML as well, the easiest way to convert from one to the other is of course to use an XSL Transformation (XSLT). This XSLT will simply output the InfoPath XML document, while inserting the data from our generic XML file in the right places. The easiest way to create the XSLT is to start from the target InfoPath XML: use the InfoPath client (or SharePoint Forms Server) to create an empty form. Then open the resulting XML file using your favorite text editor, and copy the whole XML skeleton into the body of your XSLT. The result, including the processing instructions, will look like this:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="row">
    <xsl:processing-instruction name="mso-infoPathSolution">
      <xsl:text>name="urn:schemas-microsoft-com:office:infopath:Status-Reports:-myXSD-2005-09-22T20-42-56" solutionVersion="1.0.0.3" productVersion="12.0.0.0" PIVersion="1.0.0.0" href="http://w2k3sp2-spdev/SiteDirectory/infopath/Status%20Reports/Forms/template.xsn"</xsl:text>
    </xsl:processing-instruction>
    <xsl:processing-instruction name="mso-application">
      <xsl:text>progid="InfoPath.Document" versionProgid="InfoPath.Document.2"</xsl:text>
    </xsl:processing-instruction>
    <my:statusReport xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2005-09-22T20:42:56" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003" xml:lang="en-US">
      <my:reportDate>
        <xsl:value-of select="Date"/>
      </my:reportDate>
      <my:projectName>
        <xsl:value-of select="Project"/>
      </my:projectName>
      <my:preparedBy>
        <xsl:value-of select="Prepared_x0020_By"/>
      </my:preparedBy>
      <my:emailAddress>
        <xsl:value-of select="E-Mail"/>
      </my:emailAddress>
      <my:managerName>
        <xsl:value-of select="Manager"/>
      </my:managerName>
      <my:departmentName>
        <xsl:value-of select="Department"/>
      </my:departmentName>
      <my:summary></my:summary>

Note that we insert our original XML file element values into the XSLT output, using xsl:value-of in the right places. Now, all we need to do is to actually apply this XSLT to our XML document:

            XslCompiledTransform transform = new XslCompiledTransform();
            transform.Load("CSV2InfoPath.xslt");

            foreach (XmlNode n in doc.DocumentElement.ChildNodes)
            {
                string fileName = n.ChildNodes[2].InnerText + ".xml";
                string filePath = "C:\\temp\\" + fileName;
                StreamWriter writer = new StreamWriter(filePath);
                transform.Transform(n, null, writer);
                writer.Close();

The key here is the XslCompiledTransform.Transform() method, that actually executes the transformation. Note that we iterate over our document's child nodes (i.e. row elements) and apply the XSLT to each chunk. In this example, the result is written to a temporary file on the disk, so that you can actually test the result by opening it with InfoPath.

Congratulations, you just converted a CSV data export into a bunch of InfoPath forms!

3. Upload the forms to SharePoint

The last part of our little operation is to actually publish the forms to a SharePoint Server, so that employees can open and update the forms using Forms Server (allowing them to complete these tasks using a Web browser, instead of having to edit the form on their desktop with the InfoPath client).

Since my batch program was intended to run on the SharePoint server itself, I was able to use the SharePoint API directly to upload the form. The code is fairly straightforward:

                SPWeb web = new SPSite("http://w2k3sp2-spdev/SiteDirectory/infopath").OpenWeb();

                FileStream fStream = File.OpenRead(filePath);
                byte[] contents = new byte[fStream.Length];
                fStream.Read(contents, 0, (int)fStream.Length);
                fStream.Close();

                web.Files.Add("Status%20Reports/" + fileName, contents);

The last part is a bit more tricky: using SharePoint 2007 new Role Assignment API to automatically apply security settings on the uploaded forms; the idea being, for example, to make sure each form can only be edited by the corresponding employee. The code will look like this:

                SPFile newFile = web.GetFile("Status%20Reports/" + fileName);
                newFile.Item.BreakRoleInheritance(false);
                SPRoleDefinitionCollection roleDefs = web.RoleDefinitions;
                SPRoleAssignmentCollection roleAssignments = newFile.Item.RoleAssignments;
                SPRoleAssignment newAssignment = new SPRoleAssignment("W2K3SP2-SPDEV\\SPUser", "email@toto.com", "SP User", "");
                newAssignment.RoleDefinitionBindings.Add(roleDefs["Read"]);
                roleAssignments.Add(newAssignment);

In this code, we first use BreakRoleInheritance() to stop inheriting the ACLs from the library. We then retrieve the current site Role Definitions (i.e. "Read", "Contribute", "Full Control", etc.) We then create a new Role Assignment (for a test user called SPUser), apply the "Read" role to it, and add it to the RoleAssignments collection for the form we just uploaded.

Posted 14 September 07 12:59 by tconte | 3 Comments   
Filed under
More waterworks

More waterworks
Originally uploaded by tomconte
In the Seattle Center. It's a beautiful day!
Posted 28 July 07 05:19 by tconte | 1 Comments   
Filed under
In the water taxi

In the water taxi
Originally uploaded by tomconte
On our way to Salty's at Alki Beach.
Posted 28 July 07 05:19 by tconte | 0 Comments   
Filed under
The view from my hotel room

The view from my hotel room
Originally uploaded by tomconte
Hotel Max is a boutique hotel with some nice views of the Space Needle.

(sent from my SmartPhone)
Posted 28 July 07 12:01 by tconte | 0 Comments   
Filed under
TechReady party

TechReady party
Originally uploaded by tomconte
Rooftop cinema



(sent from my SmartPhone)
Posted 27 July 07 06:38 by tconte | 1 Comments   
Filed under
Waterworks in Seattle

Waterworks in Seattle
Originally uploaded by tomconte
TechReady attendee party...
Posted 27 July 07 04:22 by tconte | 0 Comments   
Filed under
More Posts Next page »
Page view tracker