Sunday, January 22, 2006

Towards the end of last year our company Flexnet Consultants decided to release a set of free Web Parts that we thought might be useful to have installed in WSS/SPS sites. The first of these was a Flash slideshow based on the pictures held in a SharePoint picture library http://www.flexnetconsult.co.uk/WebParts/FlashSlideShow.htm.

We like Flash a lot as its a great way to liven up dull static pages and the content can be a lot more than 'eye-candy'

 

The first thing you need is some way to create the swf file that Flash is compiled into, in-house we use Flash MX. The actual slideshow view of the pictures wasn't a real problem as that's exactly the kind of thing Flash is good at. We use ActionScript to control the action on the Flash stage. 

The main issue was how to get a list of the pictures held in the library.

We looked at a few possible ways, one would be to pass a list of pictures to Flash using its URL or the FlashVars property, not pretty and not scaleable, the next idea was to talk to the SharePoint Web Services, this would have been ideal except Flash only really got proper support for talking to generic Web Services with the recent version 8 and we wanted to support Flash players version 6 and upwards to allow for the widest audience. We will probably create a version that supports 8 later.

That left us with one final method and it turns out to be a very easy one, the old SharePoint URL protocol available since version 1. The docs for these calls are here http://msdn.microsoft.com/library/default.asp?url=/library/en-us/spsdk11/Client_API/spURLProtocol.asp , there's a lack of useful examples but its pretty easy to use.

You pass parameters via URL to the SharePoint ISAPI DLL called owssvr.dll in the _vti_bin directory and it returns xml information either formated in a CAML style or raw data rowset style. We want raw Data so we put XMLDATA=true in the URL

As an example if you pass http://sharepoint.flexnet.ds/_vti_bin/owssvr.dll?CS=109&Cmd=Display&List={89ECD870-30EE-4E6E-B39B-B2C8CC548213}&XMLDATA=TRUE then you get this XML data back

- <xml xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema">
- <s:Schema id="RowsetSchema">
- <s:ElementType name="row" content="eltOnly" rs:CommandTimeout="30">
- <s:AttributeType name="ows_SelectedFlag" rs:name="Selection Checkbox" rs:number="1">
  <s:datatype dt:type="ui1" dt:maxLength="1" />
  </s:AttributeType>
- <s:AttributeType name="ows_DocIcon" rs:name="Type" rs:number="2">
  <s:datatype dt:type="string" dt:maxLength="512" />
  </s:AttributeType>
- <s:AttributeType name="ows_NameOrTitle" rs:name="Name" rs:number="3">
  <s:datatype dt:type="string" dt:maxLength="512" />
  </s:AttributeType>
- <s:AttributeType name="ows_ImageSize" rs:name="Picture Size" rs:number="4">
  <s:datatype dt:type="i4" dt:maxLength="4" />
  </s:AttributeType>
- <s:AttributeType name="ows_FileSizeDisplay" rs:name="File Size" rs:number="5">
  <s:datatype dt:type="i4" dt:maxLength="4" />
  </s:AttributeType>
- <s:AttributeType name="ows_RequiredField" rs:name="" rs:number="6">
  <s:datatype dt:type="string" dt:maxLength="1073741823" />
  </s:AttributeType>
  </s:ElementType>
  </s:Schema>
- <rs:data>
  <z:row ows_SelectedFlag="0" ows_DocIcon="jpg" ows_NameOrTitle="London01.jpg" ows_ImageSize="180" ows_FileSizeDisplay="10587" ows_RequiredField="Flash Test/London01.jpg" />
  <z:row ows_SelectedFlag="0" ows_DocIcon="jpg" ows_NameOrTitle="London02.jpg" ows_ImageSize="180" ows_FileSizeDisplay="7675" ows_RequiredField="Flash Test/London02.jpg" />
  <z:row ows_SelectedFlag="0" ows_DocIcon="jpg" ows_NameOrTitle="London03.jpg" ows_ImageSize="180" ows_FileSizeDisplay="14242" ows_RequiredField="Flash Test/London03.jpg" />
  <z:row ows_SelectedFlag="0" ows_DocIcon="jpg" ows_NameOrTitle="London04.jpg" ows_ImageSize="180" ows_FileSizeDisplay="6755" ows_RequiredField="Flash Test/London04.jpg" />
  <z:row ows_SelectedFlag="0" ows_DocIcon="jpg" ows_NameOrTitle="London05.jpg" ows_ImageSize="180" ows_FileSizeDisplay="19926" ows_RequiredField="Flash Test/London05.jpg" />
  :row ows_SelectedFlag="1" ows_NameOrTitle="SubFolder1" ows_RequiredField="Flash Test/SubFolder1" />
  </rs:data>
  </xml>
 
 
As you can see the data holds information such as the filesize and the subfolders, this could be used to recurse into the sub folders if you wanted.
 
This format is exactly what we need as Flash has a XML object that can load XML data from a URL.
Example code for this would be

var x=new XML();
x.ignoreWhiteSpace = true;
x.onLoad = LoadDocs;
x.Load("http://sharepoint.flexnet.ds/_vti_bin/owssvr.dll?CS=109&Cmd=Display&List={89ECD870-30EE-4E6E-B39B-B2C8CC548213}&XMLDATA=TRUE");

The XML object will call the LoadDocs function when it has completed the data download.
 
All thats left is to iterate the XML nodes and extract the picture names from the attribute ows_RequiredField
 
function LoadDocs(success)
{
   if (success){
   var eXmlRoot = this.firstChild;

   // allow for /r/n in the XML stream
   var eXmlSchema= eXmlRoot.childNodes[1];
   var eXmlData= eXmlRoot.childNodes[3];
   var eRows= eXmlData.childNodes;

   arrImages=new Array();

   for (i=0; i< eRows.length; i++){
      if (eRows[i].nodeType==1 && eRows[i].nodeName.toLowerCase()=="z:row"){

         //Skip subfolders and only show jpgs
         // to only show jpgs use (eRows[i].attributes.ows_DocIcon == 'jpg')
         if(eRows[i].attributes.ows_SelectedFlag=="0"
         {
            arrImages.push(eRows[i].attributes.ows_RequiredField);
         }
      }
   }

   }
// The list of pictures are now loaded
}
 
Now that we have the list of pictures we can then use ActionScript to rotate through them.
 
Once you have created your Flash SWF the final thing that needs to be done is to render out the OBJECT tag that will host the Flash file in the RenderWebPart event, this will look like this on the SharePoint page:
 
<OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0" WIDTH=180 HEIGHT=120 id="FlexnetSlides_WPQ4" ALIGN="">
<PARAM NAME=movie VALUE="http://sharepoint/wpresources/FlexnetConsultants.Slideshow/Flexnet-SlideShow1_2.swf">
<PARAM NAME=FlashVars VALUE="ServerBase=http%3a%2f%2fsharepoint%3a80&SiteURL=http%3a%2f%2fsharepoint&PicLib=dcebbba5-5e94-49a1-88b7-03f7625dd8e4&RootFolder=&Transition=Fade&Displaytime=5&Transitiontime=3&ScaleMode=0&MaxCachedImages=30">
<PARAM NAME=menu VALUE=false>
<PARAM NAME=quality VALUE=high>
<PARAM NAME=scale VALUE=noscale>
<PARAM NAME=bgcolor VALUE=#FFFFFF>
<EMBED src="http://sharepoint/wpresources/FlexnetConsultants.Slideshow/Flexnet-SlideShow1_2.swf" menu=false scale=noscale quality=high bgcolor=#ffffff FlashVars="ServerBase=http%3a%2f%2fsharepoint%3a80&SiteURL=http%3a%2f%2fsharepoint&PicLib=dcebbba5-5e94-49a1-88b7-03f7625dd8e4&RootFolder=&Transition=Fade&Displaytime=5&Transitiontime=3&ScaleMode=0&MaxCachedImages=30" WIDTH="180" HEIGHT="120" NAME="FlexnetSlides_WPQ4" ALIGN=""TYPE="application/x-shockwave-flash" PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer">
</EMBED>
</OBJECT>
 
The FlashVars method allow you to reference parameters as variables in the ActionScript code e.g. _root.ServerBase and _root.SiteURL
 
Future Web Parts in the pipeline are an improved Slideshow, a PhotoStrip with dynamic pictures (like the Flikr badge) and a analog/digital clock.
 
 
Sunday, January 22, 2006 4:52:53 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, January 16, 2006

I've just been going through the process of upgrading my development SharePoint sites to run on ASP.NET 2.0 and SQL server 2005.

First I installed the WSS Service Pack 2. No problems there.

Then I upgraded one of my sites to run on ASP.NET 2.0 using the nice new IIS property tab and running the stsadm -o upgrade -forceupgrade -url http://portalsiteurl command . The ststadm command updates the web.config in the IIS root directory and it also runs the contents of Web Server Extensions\60\TEMPLATE\SQL\STOREUP.SQL. This adds some new fields to tables and updates a lot of the core stored procedures.

An IISRESET later and I could browse to the WSS sites running on ASP.NET 2.0 with no problems.

The real problem came when I try to access another set of WSS sites that were running on a different IIS virtual server that gave me the dreaded Server Application Unavailable error

 

No panic as I knew from previous experience this is almost always a problem with Web.Config parsing or Application Pool configuration/identity .

Checking the Event Log gave me this

 

 

 

Fantastic, a helpful error message, it pointed me straight to the fact that the same Application Pool was in use by both Virtual Server applications and ASP.NET doesn't support loading a second version of .NET in the same worker process.

The fix is to create a new App Pool for the applications you want to run under ASP.NET 2.0. This would include any .Net applications that share an AppPool not just SharePoint.

 

 

Create a new App Pool based on the existing SharePoint one, give it a meaningful name

 

Assign it to the Virtual Server running SharePoint

an iisreset later and you should be good to go.

I'm so glad I don't try this stuff out on live servers, anymore.

Monday, January 16, 2006 11:43:12 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
 Sunday, January 15, 2006

One Click Minimize/Restore for WebParts

One anoyance I have with WebPart pages that have a lot of WebParts is the need to click the WebPart menu to select Minimize or Restore when you want a little more screen real estate or you need to expand a previously minimized WebPart.

To solve this niggle I've developed a simple WebPart that using client side Javascript that traps the double click on a WebPart title and toggles the state of the WebPart. It integrates with the SharePoint client side object model so any changes are saved to the site and persists across sessions.

Unzip the DoubleClick WebPart Minimize/Restore DWP file to a directory of your choice

Browse to the SharePoint WebPart page - Click the 'Modify My Page' - 'Add Web Parts' - 'Import menu' option

Click the Browse button and select the DWP file.

Click the upload button - once uploaded you can drag the WebPart to anywhere on the page.

 


Here's a little more detail of the SharePoint client side scripting.

First I needed to get a list of WebParts on a page. Thats easy - every WebPart page that holds WebParts has a global object WPSC that is initialized like this WPSC.Init(document). The object WPSC is defined in IE55up.js/IE50.js/NonIe.js depending on browser. This is the Web Part Page Services Component which handles the client side WebPart infrastructure, it provides services such as loading/saving properties and handles page events see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/spptsdk/html/wpscaOverview_SV01098760.asp  for more details.

OnPage javascript adds every WebPart on the page to its WebPartPage.Parts collection e.g.

WPSC.WebPartPage.Parts.Register('WPQ1','8db7d8d8-b1e0-4e9d-84ff-0b266b8c0beb',document.all.item('WebPartWPQ1'))

To get a list of all the WebParts on the Page you can do this

for(var i=0; i< WPSC.WebPartPage.Parts.Count;i++)
  {
    wp=WPSC.WebPartPage.Parts.Item(i);
}

What we really need is the WebPartQualifierproperty of the WebPart which is the unique ID for each WebPart on the Page, various parts of the WebPart are then suffixed with this identifier e.g WebPartTitleWPQ2 for the title div and WebPartWPQ2 for the actual serverside rendered HTML div container.

So to hook the DblClick event we can modify the inner loop like so

    wpq=WPSC.WebPartPage.Parts.Item(i).WebPartQualifier;
    var oTitle=document.getElementById('WebPartTitle' + wpq);
  
    if (oTitle)
    { 
       oTitle.attachEvent('ondblclick',MinRestoreWP);
    }

Here we're setting the double click event to fire the MinRestoreWP function. this is defined as

function MinRestoreWP()
{
var WPQId, WPObject;
var titleid,  WPQPos;

// Clear the selection the double click produces
document.selection.empty();
window.event.cancelBubble = true;
window.event.returnValue = false;

titleid=window.event.srcElement.parentNode.id;
WPQPos=titleid.lastIndexOf("WPQ");
if (WPQPos>0)
{
   WPQId='WebPart' + titleid.substring(WPQPos, titleid.length);
   WPObject=document.getElementById(WPQId);
   // call the MSO api to do the work
   if (WPObject) MSOLayout_MinimizeRestore(WPObject);
}
 
return false;
}

Here we get the WebPart to collapse or expand by going to the parent element of the Title bar which holds the WebPart title id which we can parse to get the WPQ id. This allows us to get the WebPart object we need.

The expand collapse is handled by a SharePoint function MSOLayout_MinimizeRestore which simply takes a WebPart object and does the hard work of hidding or showing the webpart by changing the WebPart.style.display CSS property. It also logs the change so the property is saved back to the WebPartPages webservice and is remembered if you close the browser and revisit the page.

DoubleClickWebPartMinimizeRestore.zip (1.35 KB)
Sunday, January 15, 2006 6:21:03 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [6]  | 
 Saturday, January 14, 2006

hi and welcome to my blog.

I'm Colin Byrne a SharePoint Architect at Flexnet Consultants based in Surrey UK.

When I was a kid I'd always get myself into trouble by taking apart anything I could, electrical, mechanical it didn't matter, you could follow the trail of screws to the corner where I had successfully taken apart plugs, hoovers, walkmans, irons.

Of course I could never put them back together again but as I was a kid that was someones else problem alas now I'm older its my problem if things don't work after I play with them. Unfortunately for SharePoint i've taken a severe liking to it and the old curiosity is kicking in so I'll be delving into the inner working of SharePoint but along the way hopefuly produce some free WebParts and utilites that can make users and admins life easier.

One of the things I also like to do is make SP sites a lot less static so adding Flash based WebParts with dynamic content is something I like to do. I'll be blogging about developing a SlideShow WebPart using Flash in the future, also new technologies such as AJAX and Atlas, well its not that new SharePoint already uses the XMLHTTP object to save and load WebPart properties but more of that later.

Oh have I mentioned I hate having to do the same thing twice.

 

Saturday, January 14, 2006 8:35:50 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
 Wednesday, July 20, 2005

Be sure to visit all the options under "Configuration" in the Admin Menu Bar above. There are 16 themes to choose from, and you can also create your own.

 

Wednesday, July 20, 2005 7:00:00 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  |