20 Oct 2008

What's new in SQL Reporting Services 2008

On Saturday I did a 20min (which is basically nothing) presentation on what is new in SRS 2008 at Dev4Devs! The feedback I have gotten has been very positive and I personally learnt more about what it takes for me to present well. That said some people asked me afterwards about the slides and content I used and the reality is that I didn’t have a single slide! The truth was Eben indicated when I volunteered that developers don’t like slides, so I took it as a challenge and did all my “slides” in SRS! For those who couldn’t attend, here is the run down on what I covered!

Introduction

The first “slide” was really about what I was covering and also some of the user groups out there! I must say sorry to Craig for leaving out www.sadeveloper.net. For those wanting the links to the groups they are:

srs1

For the Designers

Building the slide also gave me the platform for the first section, what  is new for designers! So switching to edit mode I was able to demo the fact that textboxes can now contain rich text, so the entire of the title and agenda was a single textbox with different font styles and positioning. Below that you can see the communities worth supporting with two different colours! In SQL 2005 you were limited to a single font configuration per textbox so to do the above in 2005 would have take 7 textboxes!

Next I showed off the new design surface improvements which make it no longer feel like a annoying grid but a real smooth surface. There is also enhancements like the guide lines which snap you to other design elements and the distance tooltips which show your distance from other elements (see below).

srs2

I then showed off my favorite feature, UNDO and REDO. I know it seems small but the UNDO in 2005 reloaded entire reports and took forever to do, now there is a real instance UNDO!

We’ve been Robbed

Next I went into what had been taken out… sort of Smile First was the data tab (missing highlighted in red below) which has moved to it’s own window. It feels so much slicker there! And then I spoken about the fact Table, Matrix and List were gone and what the table, matrix and list tools are now! I won’t retell the story on that because Teo Lachev has a better description at his blog.

srs3

Pretty Pictures

From there I moved into showing off the new Dundas based charting and gauge controls which make your reports look super slick and demo'd why gauges are great for showing multiple pieces of data at the same time (in my demo the distance and time of my exercising):

srs4 

The Boring Slide

Lastly I ended with a “slide” on things I couldn’t demo but are noteworthy and I included a smiley rating scale on how noteworthy they are:

srs5

  • No More IIS: SRS 2008 no longer requires IIS to run! It actually has it’s own web server and this means that not only does it scale better, it also is a true middle tier application.
  • Memory Management: Because SRS 2008 is in charge of everything now and not needing IIS, you can limit how much RAM is used!
  • Data Driven to SharePoint: You can now use a data driven subscription (i.e. one which is based on data in the DB) to publish to SharePoint.
  • Support for Teradata: A lot has been said on this, so I assume it is important (I saw some heads nod when I spoke about it in my talk), but as I have never used Teradata it got the confused smiley.
  • Per page rendering: This is big, it no longer renders the entire report at once. Now it just renders a page at time! Great for those massive reports.
  • Custom and forms based authentication: A really great feature for hybrid environments! Also combined with no more IIS those Kerberos issues between CRM and SRS should be a thing of the past!
  • Export to Excel: In my series on complex report building, the last part mentioned the horror that was exporting to excel and how sub reports generated ugly grey blocks! Well that is no longer the case. YEAH!
  • Export to CSV: Has been improved to export just data. I did point out that there is limitations (like values from gauges will not be included if you use CSV) so be careful.

I lastly mentioned a new tool, called Report Builder which is an 18Mb download and gives an Office (ribbon bar) experience to building 2008 reports. It is really great in that it has low overheads (no Visual Studio requirements), it has all the design surface features I mentioned at the start and it is very easy to get up and running. It does require a full SRS server to be available if you want to run the report, so no preview mode like in SQL BI Studio. That said it is great for power users and I see the real value coming in the future when you need to work with old reports and don’t want to install old versions of Visual Studio, like we have to with 2003 and 2005! I mentioned on Saturday it was RC1 status, we’ll it seems that was a lie because at 3:30am Saturday they released the RTM version!!!! For more and to download it (it’s free) see: http://blogs.msdn.com/robertbruckner/archive/2008/10/17/report-builder-20-release.aspx

Finally thanks for Eben and Ahmed for arranging the event and EVERYONE who attended!

Tags: 
15 Oct 2008

S.A. Architect - LINQ Drilldown

One of the perks of being a registered member of S.A. Architect is that when the ATC team at Barone Budge and Dominick run a training session we keep a seat open for a community member. Coming up at the end of this month is my first full day session *gulp* which aims to cover LINQ. Being the shameless self promoting type, I thought I would do a little blurb on what you can expect if you, said S.A. Architect member, decide to join us.

Remember attendance for this one member is FREE! First you come to our excellent collab centre where you will have your own PC to work on for the day (sorry, can’t take it home with you) and I will be taking you through 28 HANDS ON labs during the day! Most are small labs (sub < 15min) so don’t worry about leaving late. Mindful that you are here for the full day, you get a free hot lunch (always cool) and I promise to personally guide you to the vending machines (snacks and coke) which will be free too! You will also take home a printed copy of the manual created for this session. I am waiting for the final proof to be developed but I suspect it will be about 200 pages in length in the end! Next I will be asking questions during the day (to see who is sleeping) and for prizes there the stress balls. I am hoping we will have a few t-shirts left after Rhodes to give away to people answering questions too. Lastly you get to meet some very smart people and do some networking!

Before it sounds like this is super special for my session, most of this (lunch, snacks, prizes, networking) is available at most of the full day sessions we run!

This is a session for someone who knows nothing of LINQ and we will start at the basics and go through to some level 300 stuff in the end. You do need to know basic C# (if you know what I mean by saying: add the using System.Data.Linq, you will be fine). The day will cover:

  • The Problem which LINQ solves
  • LINQ Architecture
  • Implicitly Typed Variables
  • Use LINQ to Objects to get and sort data and understand what is happening using the basic LINQ program.
  • Anonymous Types
  • LINQ Query Execution
  • Anonymous Methods and Lambda Expressions
  • LINQ Query Operators
  • LINQ to XML
  • LINQ to SQL

So if you are interested, first register at S.A Architect. Then let Willy know you wish to attend! Dates/times etc… can be found here!

Tags: 
15 Oct 2008

Dev4Devs - This Saturday

A few weeks ago I posted about the Dev4Devs event coming up, well it is this Saturday! I will not only be attending but I will also be doing a presentation on what’s new in SQL reporting services (SRS) in 2008! It will be a nice quick level 100 to 200 demo of the new stuff.

I also saw on Eben’s post that two of my absolutely favorite presenters are speaking, namely Rudi Grobler (he is speaking on what’s new in 3.5 SP 1 for client development) and Brent Samodien (he is speaking on consuming ASP.NET data services using AJAX… which I hope means he will show ADO.NET Data Services, will have to wait until Saturday to find out).

Tags: 
14 Oct 2008

Rhodes students, you lucky bunch!

Willy posted about the upcoming trip to Rhodes (see here) which is really interesting. I am not going along for the road trip but I have had a chance to see what swag will be given away and let me state that those at Rhodes who are attending are going to be the envy of those who do not attend. Not only is there the standard flyers and free pens, but a great t-shirt (I got my hands on a development one! There is also three others… but that can be surprise) and a DVD with over 3.5Gb of content. I have been looking at the DVD content and there is not only full copies of the 3 books Willy has written on there but copies of all the content from S.A. Architect! Personally I rate that swag is as good as what you would find at a major event like Tech-Ed!

08 Oct 2008

WSS and audience targeting - Part II

Today is NOT my day for things just working. Besides the demo gods eating every demo I did today and presenting me with excrement to work with, I found out that the super cool WSS audience targeting is broken on every browser except IE :( There is no use in complaining so silver lining is that I do get a chance to solve the problem and learn something new about browsers.

First off is that XmlHttpRequest is supported by all browsers, the XML DOM implementation is not compatible so even though you can get the data you can’t work with it easy. The cause is that Firefox or Chrome do not support selectNodes or selectSingleNodes methods or IE has unique methods which no other browser supports (depending which camp you are in). selectSingleNode is what I use to parse my results! To solve this I found some code at http://km0.la/js/mozXPath/ which adds the methods to the JavaScript classes! This is great in theory but it didn’t work because I have a default namespace being returned by my XML! The code I found didn’t work as selectSingleNode always returned null because it needed the namespace prefix on the XPath (i.e. /default:user) which IE can’t work with. So that meant not only implementing a new resolver which I found out about on developer.mozilla.org, it also meant doing a check and doing two XPath queries.

Something I didn’t mention in the first post is security and how secure or insecure this method is. Your biggest attack vector on this is that it runs client side and that the LoadXmlDoc method or even just the length check on the username can be changed easily so that it shows the content for logged in users to users who are not logged in! Basically it is not secure, but it doesn’t mean it opened a security hole because so you need to think about what is shown.In my case I made sure what is show is the bare minimum, just links to other pages. If an attacker gets the link they shouldn’t it does not worry me because the change password page, which signed in people can click to, is protected by WSS’s security. So even if someone were to try and access it they would get denied! The page itself also has security on it to help prevent security issues. The point I want to emphases is that this is NOT a security model, but a model for a better user interface. 

 

<script type="text/javascript">
    // Created by RMacLean - for comments email [email protected]
    // Partially from http://www.w3schools.com/XML/xml_http.asp
    // Partially from http://en.wikipedia.org/wiki/Xmlhttprequest
    // Partially from http://km0.la/js/mozXPath/
    // Partially from http://developer.mozilla.org/en/Introduction_to_using_XPath_in_JavaScript#Implementing_a_User_Defined_Namespace_Resolver
 
    // check for XPath implementation
    if (document.implementation.hasFeature("XPath", "3.0")) {
 
        // prototying the XMLDocument.selectNodes
        XMLDocument.prototype.selectNodes = function(cXPathString, xNode) {
            if (!xNode) { xNode = this; }
 
            var oNSResolver = document.createNSResolver(this.ownerDocument == null ? this.documentElement : this.ownerDocument.documentElement);
            function resolver() {
                return 'http://schemas.saarchitect.net/ajax/2008/09/user';
            }
 
            var aItems = this.evaluate(cXPathString, xNode, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
            var aResult = [];
            for (var i = 0; i < aItems.snapshotLength; i++) {
                aResult[i] = aItems.snapshotItem(i);
            }
            return aResult;
        }
        18.
        // prototying the Element
        Element.prototype.selectNodes = function(cXPathString) {
            if (this.ownerDocument.selectNodes) {
                return this.ownerDocument.selectNodes(cXPathString, this);
            }
            else { throw "For XML Elements Only"; }
        }
 
        // prototying the XMLDocument.selectSingleNode
        XMLDocument.prototype.selectSingleNode = function(cXPathString, xNode) {
            if (!xNode) { xNode = this; }
            var xItems = this.selectNodes(cXPathString, xNode);
            if (xItems.length > 0) {
                return xItems[0];
            }
            else {
                return null;
            }
        }
 
        // prototying the Element
        Element.prototype.selectSingleNode = function(cXPathString) {
            if (this.ownerDocument.selectSingleNode) {
                return this.ownerDocument.selectSingleNode(cXPathString, this);
            }
            else { throw "For XML Elements Only"; }
        }
    };
 
    // Provide the XMLHttpRequest class for IE 5.x-6.x:
    if (typeof XMLHttpRequest == "undefined") XMLHttpRequest = function() {
        try { return new ActiveXObject("Msxml2.XMLHTTP.6.0") } catch (e) { }
        try { return new ActiveXObject("Msxml2.XMLHTTP.3.0") } catch (e) { }
        try { return new ActiveXObject("Msxml2.XMLHTTP") } catch (e) { }
        try { return new ActiveXObject("Microsoft.XMLHTTP") } catch (e) { }
        throw new Error("This browser does not support XMLHttpRequest.")
    };
 
    var xmlhttp;
 
    function loadXMLDoc(url) {
        xmlhttp = new XMLHttpRequest();
 
        if (xmlhttp != null) {
            xmlhttp.onreadystatechange = state_Change;
            xmlhttp.open("GET", url, true);
            xmlhttp.send(null);
        }
        else {
            alert("Your browser does not support XMLHTTP.");
        }
    }
 
    function state_Change() {
        if (xmlhttp.readyState == 4) {// 4 = "loaded"
            if (xmlhttp.status == 200) {// 200 = OK
                var username = "";
 
                if (xmlhttp.responseXML.selectSingleNode('//user') == null) {
                    username = xmlhttp.responseXML.selectSingleNode('//myns:user').getAttribute('username');
                }
                else {
                    username = xmlhttp.responseXML.selectSingleNode('//user').getAttribute('username');
                }
                if (username.length > 0) {
                    // user logged in
                    document.getElementById('resultText').innerHTML = '<P><A href="/memberPages/changepassword.aspx">Change Password</A></P>';
                }
                else {
                    // anonymous
                    document.getElementById('resultText').innerHTML = '<P><A href="/Pages/signup.aspx">Signup</A><BR><A href="/Pages/forgotpassword.aspx">Lost Password</A></P>';
                }
            }
            else {
                alert("Problem retrieving XML data");
            }
        }
    }
 
    loadXMLDoc("/Pages/loggedinuser.aspx");
</script>
 
<span id="resultText">Loading...</span>
Tags: 
06 Oct 2008

Podcasting Kit for Sharepoint + Sub Site = It can be done

Recently I aged about a hundred years due to my intension to deploy the August 2008 release of the Podcasting Kit for SharePoint on a sub site due to the fact that there is so much hard coded into it (you can vote on that link for the work item to be done to fix this) that expects it to be at the root. Being stubborn and trying to show my elite SharePoint skills to all around me, I did not let little things like hard coding values stop me. No, I gave up a few years of my boyish good looks to get it working and in the end I did :) It’s not elegant but it works and should tide you over until the fixed release comes along.

First off let me say how great the August release is compared to the July release, it’s great. I actually don’t think you could’ve got this to work on the July release, there is that much cleanup and streamlining in it. One of the things they did was streamline the documentation from a monolithic single beast to a lot of smaller edible chunks. This is also a double edged sword for first timers because it means you go through the install doc and think you are done… when you aren’t, but I shall cover that later on.

For the tale assume the following facts:

  • SharePoint is deployed to a site available at http://intranet
  • We want the Podcasting kit to be available at http://intranet/sites/multimedia
  • I have created a life jacket file which contains versions of all the files I changed and may be of use to you. Note I have not tested it so it may not work. Best case is a simple find and replace and upload and you are in business, but worst case you need to follow the steps to get your files done. Download the life jacket file HERE
  • I am insane and this could all be wrong and my mind has made me believe it works.

Now I want to tell my tale of heroism at defeating bugs so that future crazy people don’t need to do this themselves. If you have the August 08 install guide and are following along (good idea, since that’s what I am doing to write this) I assume you have done your prerequisites and have a site collection as a sub site (i.e. http://intranet/sites/multimedia) and are at Installation Method #1 (Easy Method). Easy method that in itself should be the sign of the devil which awaits, because anything labeled easy never is. What you need to do is run the install to the root site (i.e. ssm.exe install PKSFull.xml http://intranet) and then uninstall it (i.e. ssm.exe uninstall PKSFull.xml http://intranet). I know it seems pointless but the installer doesn’t clean up well and leaves a few files behind (The key stuff it leaves is CSS’s, XSL’s and some JS files), and having these files at root helps us later on. Now run the installer again but now to your site collection (i.e. ssm.exe install PKSFull.xml http://intranet/sites/multimedia).

The next steps in the guide of the ratings DB, media encoder and feature activation work as documented. The user interface steps also work as expected but make sure you “fix” their links to have your site collection. So for a few examples:

  • SmartPhone access make the URL /sites/multimedia/mobilepages/pksmobilehome.aspx
  • Upload Podcasts make the URL: /sites/multimedia/PKS Podcasts/NewForm.aspx?RootFolder=%2FPKS%20Podcasts&Source=/sites/multimedia/pages/pkshomepage.aspx

a1Moving swiftly along the master and welcome pages instructions should work fine and that leads us to the first big hurdle the SmartPhone page because this is where the hard coding comes in. See the PKS uses a special web part called the Content Query Override web part, which is like the out of the box Content Query web part but allows you access to all the properties you couldn’t normally get to. However the PKS team set these up on their side and when you import it you are assured of doom and destruction if you are on a sub site. One of the properties that is hard coded is the path to the web URL for the site.  If you simply open the pksmobilehome.aspx page, find the weburl tag and change it to be your site path (in my case /sites/multimedia) prior to uploading the file, as per the document, you will be fine and continue through the document happily following the sections on Profiles, Silverlight 2.0, Rating and Commenting, External File Store, and Media Encoder Service to bring yourself merrily to the Pages section.

Before heading into the pages section we need to fix up the XSL files so they point correctly. You can find these in All Site Content –> Style Library –> XSL Style Sheets. Best is to switch to explorer view get all the files out of there and open them all up in text editor which allows Find & Replace across multiple files (I used Visual Studio, how developer of me) to fix the URL’s up. There are hundreds of the URLs so I suggest doing find and replace to fix it. As I said earlier all changed files are in the life jacket file, which includes these. It may be easier to use mine since you just need to search for /sites/multimedia and replace it with what ever your URL is. Use it, don’t use it, whatever. Once done re-upload the files and then make sure you check each file in as a major version.

The reporting page works great but you will find you are stuck at the podcasterdetail.aspx page because you can’t actually get to those settings. Once again this has to do with the fact that this web part is the content query override webpart. This one is a little worse than the mobile page because it has been pre-configured and is incorrect. To solve it, prior to editing the settings export the web part and save it to disk then open it up in your favorite text editor and make sure all the paths are correct, those being:

  • ItemXslLink
  • WebUrl
  • MainXslLink
  • ItemXslLinkOverride
  • MainXslLinkOverride
  • Xsl

Once you have done that you can upload the web part (either to the web part gallery or direct on the page) and you should be able to work with it successfully now.

Moving to the podcastdetail.aspx make sure you set the URL correct for the Xslt Override Location on the Content Rating-Review Results web part. You can/should skip the 9 to 15 (about the content editor web part which shows the edit link) since it references a JS file I have yet to be able to find and fix so it will point to the wrong URL and it will not work. If you do fix this please let me know. Setting up the web connections on the page should be fine and so should the search. If you do not have the metadata properties, like in my case, please read the troubleshooting guide. It’s likely you assumed too much and thought life was easy… easy install and all ;)

That should take you to the end of the guide, but not the end of the of our tale. If you recall I mentioned the double edged sword of splitting the documentation well the next step is not obvious. You should now go through the How To Apply Security Settings file and follow that because if you don’t you will have lots of crap. You should also be aware of a bug in the PKS which can prevent any non-admin like people from viewing/downloading content see here for details on that.

a2

a3Right, now that the security is sorted we can get back to getting the site working as a sub-site. The next step is to fix the configuration so go to All Site Content and go to Content Rating Configuration Settings. You were here earlier and find the item you copied from before (the one that is a GUID@GUID) and make sure the value points correctly. Now head back to All Site Content and go to PKS Configuration Settings and set the various values in their to point to the correct URLs, this includes:

  • DownloadTracking.Location
  • ErrorHandling.InvalidMediaFile.Image
  • ErrorHandling.NoVideoStream.Image
  • Thumbnail.Custom.Images (not you have multiple URL’s in the value here, make sure you get them all).

 

Now you have the main page pkshomepage.aspx which suffers from the same issue as the podcasterdetail.aspx page in that the content query override webpart is incorrectly configured. You can follow the same procedure as before (export, edit, upload) to solve it.

At this point everything should look like it works fine, but there is a bug lurking under the covers. Anytime anyone watches a video it will increment the download count which is stored on the file entry in the PKS Podcasts list, this update of the list item causes the File URL to change and even though originally it pointed correctly to /sites/multimedia/_layouts/MSIT.customfiles/Download.aspx?…  it gets updated to point to the root site again, in other words /_layouts/MSIT.customfiles/Download.aspx?…

Before: a4

After: a5

I am not sure all the circumstances around the bug (it doesn’t seem to effect admins). To solve this I wrote a quick ASP.NET page which will redirect requests back to the correct URL, the file is in the life jacket file and is called downloads.aspx. Now if you have a different sub site path (i.e. not /sites/multimedia like me) then you need to open that file up in a text editor and change the URL as needed.

a6

Important Notes:

  • YOU DO NOT NEED VISUAL STUDIO FOR THIS. The file is a single ASPX page with the code in it, the server will compile automatically at runtime.
  • The fact it points to a file called realdownload.aspx is correct.

Now get on to your SharePoint server (if you aren’t there already) and navigate to C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\template\layouts\MSIT.CustomPages. This folder should have a single file called download.aspx, which you will rename to realdownload.aspx and copy the download.aspx file from the life jacket file into this folder. So all requests will hit the custom file which will redirect them back to the correct file. This should fix up the issue caused by the bug.

Now finally at this point you are actually done! You can now go through the documents on workflow and series and get all your content up and (hopefully) enjoy your Podcasting kit!

I want to thank a few people who helped me along in getting this right namely Michael Gannotti, Zlatan Dzinic, and of course the PKS team who responded to my posts on the site!

Tags: 
06 Oct 2008

Slide.Show and SharePoint: Part II - Getting the data

This is a multi-part series. The other parts can be found at

In part I we focused on getting Slide.Show to work with the content editor web part (CEWP), now the next step is getting the image information out of of the picture library so Slide.Show can display it. The first point you’ll need to understand is how Slide.Show knows where to find the images. It does it with a DataProvider, which is really a XML file reader. In the default configuration it looks for a file in same directory as the configuration file named Data.XML for the information, but it can be overridden with a custom DataProvider, like the Flickr one which was used in the last step of part I. Unfortunately there is no SharePoint data provider yet so we need to either roll our own version or provide the SharePoint data in a format Slide.Show can work with natively. Both require development but I prefer the second option because it means that I am not investing massively into Slide.Show, rather I am investing my time into a tool to get the data out and then a small bit of time into formatting it for Slide.Show. The advantage of this means that should a better solution come along in the future I can easily change the formatting part and have it work with the newer system. The other advantage of the second option is a personal one, because I prefer C# development (which I will build the provider in) with all of Visual Studio’s helping and documentation to JavaScript development (which is what would have to be used for the DataProvider).

The XML format which Slide.Show requires is something like this:

   1: <data>
   2:     <album ...>
   3:         <slide .../>
   4:         <slide .../>
   5:         <slide .../>
   6:         ...
   7:     </album>
   8: </data>

SharePoint won’t give the data to us in the format so to solve this I wrote an ASP.NET page does the following:

b1

The Get stage is fairly logical in that we connect to the Lists web service in SharePoint and using the GetListItems method we retrieve the items from the list which returns as an XMLNode, which is not the easiest way to work for two reasons. Firstly SharePoint has a lot of namespaces so using XPath to extract the items is tricky and annoying, but thankfully John Wood did post a nice way to work with the XMLNamespaceManager which I use. The second reason comes back to the reason why I am doing this in C# as opposed to a dedicated Slide.Show provider. Once I convert it to easier to work with structure I can do the outputting to various systems a lot quicker. So in this case I created a simple class which contains a few properties to define the slide image and pop that into a List<T>. I am a bit of a List<T> fan boy using it where I can because it is fast and it allows me to use Linq with it easily (yes I know I can use Linq To XML but I prefer the rich integration in VS of Linq to objects and the thought of SharePoint’s XML scares me), and using Linq will allow for some nice features later on.

ClassDiagram

The last stage Provide is where I take my list of slides and use it to build up an XMLDocument which I will return. The building of the XMLDocument is not rocket science (just a couple of foreach’s really), but the returning it may be of interest to some people so I will just cover that briefly. What I do is drop the PageLoad method of the ASP.NET page and work in the render method which allows me to get into the page life cycle earlier on and control a few more parts of the output using the Response class. There are only two things I really care about, first is Response.ContentType which specifies to the caller (normally your browser but in this case Slide.Show’s DataProvider) what the content is. So we set that to Text/XML. This is one of the first things I do, and then one of the last things I do is send the XMLDocument.OuterXML to the browser using Response.Write. Provided I have done all my stages correctly I should be able to browse to the ASP.NET page and see XML formatted as I wanted it.

b2

Part of building this also means making it reusable so there is a couple of things I did. First off the security is handled using built in Windows Authentication which may be a problem on web facing sites but it does mean that on intranet sites the security of the list items is maintained which is vitally imported. This is easily done by getting the configuration on IIS right (which we will cover in part III) and setting the Lists.UseDefaultCredentials to true. The next two vital configuration items are the URL to the lists web service and the name (or GUID) of the list we want. This is done using mandatory query string parameters (failure to provide them throws an AgrumentException) so your most minimalistic query string would look like this:

http://sharepoint/addon/slideshow.aspx?url=http://sharepoint/site/_vti_bin/lists.asmx&list=Photo%20Gallery

Breaking that down it has the following parts

I have added support for a few extra optional items to the query string as well which allows you to tailor the results:

  • view : Used to specify the view name (or GUID) you want returned. Leaving it out returns the default view. Type is a string.
  • limit : Used to specify the number of items to return. Default is all items. Type is a integer.
  • recurse : Used to specify if you want to recurse into folders or just return the items from the root. Default is to recurse. Type is a boolean.
  • group : Used to create Slide.Show albums based on the folders. Default is false. Type is a boolean.
  • random : Used to return the items (not albums, if group is true) in a random order. Default is true and type is boolean.

An example of using some of those options would look like this: http://sharepoint/addons/slideshow.aspx?url=http://sharepoint/site/_vti_bin/lists.asmx&list=Photo%20Gallery&group=true&random=false&limit=20

b3

So the default provides all images (regardless of folders) in random order which looks great with the standard Slide.Show settings (lots of images, randomly displaying etc..).

That is the hard part of this, the last part is getting this web application to deploy on the server (some things to be aware of) and configuring Slide.Show to use it, which is surprisingly easy.

29 Sep 2008

WSS and audience targeting

If you follow Willy’s blog you would have seen a post mentioning I have been a busy little person recently (if you hadn’t see it you can go here to read it). One of the things about SAArchitect.net is that it does not run on the big boys edition of SharePoint, it actually runs on WSS. However the thing people commonly forget about WSS is that it actually can do everything it’s brother can do, it just does not have all the templates and the fiddly bits out of the box. This means the time/cost/pain to get to the same point as it’s brother is a bit/minor/average/excessively more. One of the fiddly little bits which is missing, is audience targeting. Which I think is super sexy for public web sites, especially when you want to show different things to users who have logged in or and those who haven’t. An example is on the new SAArchitect site when you are logged in it should let you change your password and if you aren’t logged in then it should let you apply or request a forgotten password.

So putting my developer cap on (or at least adjusting it since it doesn’t ever come off) I fired up my trusty copy of Visual Studio. I needed VS as I had decided to solve this using ASP.NET and a web part which does come with WSS, the Content Editor Web Part (CEWP). Utilizing all of the power of Visual Studio 2008, I toiled away to produce my ASP.NET page with a staggering 22 lines of code which looks something like this:

Note: All the code is available in the file at the end of this post.

   1: <%@ Page Language="C#" ContentType="text/xml" %>
   2:  
   3: <%@ Import Namespace="System" %>
   4: <%@ Import Namespace="System.Globalization" %>
   5: <%@ Import Namespace="System.Web.UI" %>
   6: <script language="C#" runat="server">
   7:     protected override void OnPreRender(EventArgs e)
   8:         {
   9:             string result = string.Empty;
  10:             if (Page.User.Identity.IsAuthenticated)
  11:             {
  12:                 result = String.Format(CultureInfo.CurrentCulture, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
  13:                     "<user xmlns=\"http://schemas.saarchitect.net/ajax/2008/09/user\" username=\"{0}\" />", Page.User.Identity.Name);
  14:             }
  15:             else
  16:             {
  17:                 result = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
  18:                     "<user xmlns=\"http://schemas.saarchitect.net/ajax/2008/09/user\" username=\"\" />";
  19:             }
  20:             Response.Write(result);
  21:         }
  22: </script>

For those out there looking at that code and thinking it looks a little odd, it could be that the code all goes in the .aspx page itself. No code behind magic or web.configs or app_data folders to weigh this down. At 22 lines of code it’s all very simple: just checking if you are authenticated and if you are it returns an XML fragment with your name in it, and if you aren’t a XML fragment with no name and even a pretty namespace. In reality that could have been smaller like <u a=”0”/> if you aren’t logged in and  <u a=”1”/> if you are, but that isn’t very human readable ;) That single file sits on the WSS site in a document library, which is interesting (at least to me) because SharePoint will not let the code run by default as a way of protecting you from security issues. However if you are smart/brave/stupid enough you can tell SharePoint to let you run it, using the SharePoint web.config. Microsoft has a great article on how to do that over there.

Now that I have the code to tell me if I am logged in or not, I need to hide/show content based on it, which is easily done using the source editing options of the CEWP to plop in a bit of JavaScript. This bit of JavaScript uses a XMLHttpRequest to call the ASP.NET page and gets the result, which I check and based on the result show the relevant content as described below:

   1: <script type="text/javascript">
   2: //Partially from http://www.w3schools.com/XML/xml_http.asp
   3: // Partially from http://en.wikipedia.org/wiki/Xmlhttprequest
   4: // Provide the XMLHttpRequest class for IE 5.x-6.x:
   5: if( typeof XMLHttpRequest == "undefined" ) XMLHttpRequest = function() {
   6:   try { return new ActiveXObject("Msxml2.XMLHTTP.6.0") } catch(e) {}
   7:   try { return new ActiveXObject("Msxml2.XMLHTTP.3.0") } catch(e) {}
   8:   try { return new ActiveXObject("Msxml2.XMLHTTP") } catch(e) {}
   9:   try { return new ActiveXObject("Microsoft.XMLHTTP") } catch(e) {}
  10:   throw new Error( "This browser does not support XMLHttpRequest." )
  11: };
  12:  
  13:  
  14: var xmlhttp;
  15: function loadXMLDoc(url)
  16: {
  17:   xmlhttp = new XMLHttpRequest();
  18:  
  19:   if (xmlhttp != null)
  20:    {
  21:      xmlhttp.onreadystatechange=state_Change;
  22:      xmlhttp.open("GET",url,true);
  23:      xmlhttp.send(null);
  24:    }
  25:    else
  26:    {
  27:      alert("Your browser does not support XMLHTTP.");
  28:    }
  29:  }
  30:   
  31:  function state_Change()
  32:  {
  33:    if (xmlhttp.readyState == 4)
  34:    {// 4 = "loaded"
  35:      if (xmlhttp.status == 200)
  36:      {// 200 = OK
  37:        var username = xmlhttp.responseXML.selectSingleNode('//user').getAttribute('username');
  38:        if (username.length > 0)
  39:        {
  40:          // user logged in
  41:  document.getElementById('resultText').outerHTML = '<P><A href="/memberPages/changepassword.aspx">Change Password</A></P>';
  42:        }
  43:        else
  44:        {
  45:          // anonymous
  46:          document.getElementById('resultText').outerHTML = '<P><A href="/Pages/signup.aspx">Signup</A><BR><A href="/Pages/forgotpassword.aspx">Lost Password</A></P>';
  47:        }
  48:      }
  49:      else
  50:      {
  51:        alert("Problem retrieving XML data");
  52:      }
  53:    }
  54:  }
  55:   
  56:  loadXMLDoc("/Pages/loggedinuser.aspx");
  57:  </script> 
  58:  <span id="resultText"/>
  59:  

Admittedly this is not a solution that the little old lady with the white lunch box will be able to implement while she updates the bridge league site, but this might work just fine for a site about architecture which has a few smart people, a couple of MVP’s and me running around behind it.

You can download the page and the JavaScript in this file: DOWNLOAD

IMPORTANT NOTE: I have posted a follow up article which fixes some issues with this version. For more information see WSS and audience targeting - Part II.

Tags: 

Pages