Sunday, May 16, 2010

SharePoint 2010 explorer (using the Client object model)

This is a small WinForm tool again using the TreeView-PropertyGrid combo very much like the popular SharePoint Manager 2010 tool (which became popular with its 2007 version), but instead using the client object model which was introduced in SharePoint 2010. This is actually the second tool in a row using the TreeView-PropertyGrid setup that I release – the first one being the WebPart manager tool, that I demonstrated several weeks ago – check here (both tools actually reuse the same core visual components).

The tool can be downloaded from here (sources).

spx1

So as you can see from the screen-shot the tool shows the client object model hierarchy in its TreeView control in the left pane and when an object is selected there its properties are displayed in the PropertyGrid control in the right. Since the client object model is used, only the hierarchy below the site collection level is displayed (as opposed to the “server” object model where you have everything starting with the farm level). On the other hand the client object model which utilizes internally the SharePoint web services  allows you to connect remotely to multiple SharePoint farms as long as you have the appropriate access rights.

The tool uses extensively – first and foremost reflection – the object model hierarchy in the TreeView is actually a one-to-one mapping with the real object model hierarchy since the child nodes are actually the real member properties of the class of the parent object. Secondly – the available commands/actions for every object in the hierarchy are actually the class methods of that object which are retrieved and eventually executed with reflection. The other thing used extensively in the tool is the PropertyGrid control itself – it is used not only to visualize the properties of the currently selected object but also in the execute action dialog where it gets populated with and allows the setting of the parameters of the underlying client object class method.

System requirements and installation notes: you need .NET 3.5 and SharePoint 2010 on the machine that you use the tool on. Note that the tool uses only two SharePoint assemblies – Microsoft.SharePoint.Client and Microsoft.SharePoint.Client.Runtime – so you will be able to run the tool on a non-SharePoint machine if you have these two copied on that machine – I am not sure though whether Microsoft allow these to be distributed in this way. The tool consists of 4 files: SPx.exe, SPx.Core.dll, SPx.SharePoint.Client.dll and spxclient.xml – there’re no special installation steps, you can just copy the files to the target machine and start the executable.

General usability notes: here is a small list of some useful notes and tips for using the tool:

  • the tool’s PropertyGrid is an extended version of the original PropertyGrid control. One very useful addition to it is the ability to edit multi-line text in string properties – you can use that when you right click a grid item containing a string property of the selected object – then a small dialog box will open in which the text value of the property will appear in a multi-line text box. This is handy especially in cases when you want to edit the Field.SchemaXml or the View.ViewQuery properties.

    spx6
  • when you select a node in the TreeView the tool calls the ClientContext.Load and ClientContext.ExecuteQuery methods to load the corresponding object and its properties. Various errors can occur during loading and these are displayed in a small strip just below the PropertyGrid control: 

    spx2 
    Most notifications reflect normal error conditions in the client object model – for example the Web.AssociatedMemberGroup property may be null in the “server” object model so when you try to load it with the client object model the latter will raise an exception to that effect. Another common notification is the “Info: Not all properties are available.” one – you will see it for all Folder objects which represent non-list folders (e.g. Web.RootFolder) – in this case the Folder.ConentTypeOrder and Folder.UniqueConentTypeOrder properties are invalid in the “server” object model and consequently cannot be retrieved using the client model.
  • when you right click a node in the TreeView control a context menu with all public instance methods of the object’s class will appear (the same menu for the currently selected object is available in the toolbar below the “Actions” button):

    spx3 
    when you click on one of the “reflected” method commands a dialog box will pop up – in the dialog a PropertyGrid control will be populated with the selected method’s parameters which you can use to provide appropriate values to execute the method:

    spx4 
  • a BIG NOTE here – though all public instance methods of the selected object are available in the context menu in many cases you won’t be able to execute the selected client object method – there’re two main reasons for that – 1st – the limitations of the UI of the tool – you may simply not be able to provide values in the PropertyGrid control for the method’s parameters – note that you can insert values in the grid items only for primitive types and not for class types. Secondly – there’re cases when you need to call several client object methods consecutively (and pass the returned objects from the preceding to the next method calls) before committing them with ClientContext.ExecuteQuery – the tool normally calls the ClientContext.ExecuteQuery method immediately after you click the “OK” button in the parameters dialog, so generally you won’t be able handle more complex scenarios with several method invocations (the tool though has a switch that can be used to disable temporarily the automatic call of ClientContext.ExecuteQuery – see below).
  • despite the above mentioned limitations you can use the tool in at least these cases: create, update and delete operations for Web, List, Field, View and Folder objects; upload, delete, check in/out and publish files; add and delete content types to lists; etc.
  • the tool provides two enhancements for calling methods containing parameters of types inheriting Microsoft.SharePoint.Client.ClientObject  and Microsoft.SharePoint.ClientValueObject. In the first case you have the option to click the grid item ellipses button which will display a small popup with the same object hierarchy tree that you have in the tool’s right pane from which you will be able to select an existing in the hierarchy ClientObject that can be used in the method’s parameter:

    spx5 
    as you see in the screen-shot this is very useful when you call the List.ContentTypes.AddExistingContentType method and provide a reference to a site content type so that you can add that content type to the list.
    In the second case the tool “flattens” the parameter and instead of a single grid item for the object it displays separate grid items for every property of the ClientValueObject inheriting class. A small example for that – when you call the WebCollection.Add method to create a new sub-web, in the method’s parameters PropertyGrid instead of a single grid item containing a null WebCreationInformation object, you will see six grid items containing the properties of the WebCreationInformation class – Url, Title, Description, Language, WebTemplate and UseSamePermissionsAsParentSite.
  • the “Add return object to tree” option in the method invocation dialog – this is available for all non-void methods and allows you to dynamically add the object returned from the method call to the hierarchy tree in the right pane – the object will appear as a child node of the currently selected node (all “dynamic” nodes will disappear if you refresh the parent or any of the ancestor nodes). In most cases you won’t need to use this option since most “get” methods will return an object that you already have in the client collection object and most “add” methods will return an object which will automatically appear in the client collection (the tool will internally refresh the collection object). There’re several cases however in which this is a very handy option and the only way to get certain objects – for instance List.GetItems, List.GetItemById, File.GetLimitedWebPartManager, etc.

Commands overview:

The tool provides a pretty simple command user interface – a toolbar at the top of the main window with three menu buttons:

  • Actions menu – this is context dependant – it displays the available commands/actions for the currently selected object. You have the “Refresh” command at the top, available for all nodes and below it commands for the available public instance methods of the object’s class (see above in the “General usability notes” section)
  • Connections menu – you have two commands here: “Connect …” and “Disconnect”. The former displays a dialog in which you should provide the URL to the local or remote site collection that should be opened in the tool. You can optionally provide user credentials and authentication mode here. You can open many different site collections from different SharePoint farms. The disconnect command removes the site collection of the currently selected node from the TreeView.
  • Settings menu – you have three commands here that operate as switches – you can check and uncheck them:
    • “Add method return object to tree” – when this is checked the check box switch with the same label in the method call dialog will be checked by default. Also handy for methods without parameters for which the method invocation dialog doesn’t get launched.
    • “Suppress display object in property grid” – this will effectively disable the PropertyGrid in the left pane, the selected object won’t be displayed there. This can be handy in some advanced situations when the very querying of the object’s properties may affect its state.
    • “Suppress ExecuteQuery” – also handy for some advanced situations. Note that the tool calls ClientContext.ExecuteQuery after each node selection in the TreeView and after each method call selected from the context menu. If you use this option to disable temporarily the ExecuteQuery calls you will be able to execute several client object methods consecutively and only after that commit them with ClientContext.ExecuteQuery. Note that the objects that you use for these calls should have been loaded in advance otherwise you will receive various “not initialized” errors for collections, objects and properties. Basically you should have a very good understanding of the client object model to be able to play with this option and overall there are not that many scenarios in which it can be helpful.

Thursday, May 6, 2010

How to display all versions in a SharePoint list view page – part 2

Several months ago I wrote a small posting describing how you can trick the standard list view web part to display all versions of the list items in a SharePoint list – link. One problem though with this was that even if the list view columns display the values of the relevant item versions the “Name” column in document libraries links always to the current version of document (the same holds for the “Title” column in non-library lists and also for the commands in the context menu of the item). In one of the comments of the original posting there was a request to me to demonstrate a solution or work-around for that which is what I am going to elaborate on in this posting. The solution is applicable for document libraries only and uses a custom computed field which renders as a link to the document which when clicked opens the correct version of the selected document. The link actually calls a small custom application page (that should be deployed to the “12/TEMPLATE/LAYOUTS” folder of the SharePoint installation) which with several lines of code using the SharePoint object model redirects either to the current document version or to one of its previous ones (with the _VTI_HISTORY URL pattern).

And the implementation itself, the schema XML of the computed field:

<Field ID="{ccccdddd-3710-4374-b433-61cb4a686c12}"

       ReadOnly="TRUE"

       Type="Computed"

       Name="VersionLinkFilename"

       DisplayName="Version link"

       DisplayNameSrcField="FileLeafRef"

       Filterable="FALSE"

       AuthoringInfo="(linked to version)"

       SourceID="http://schemas.microsoft.com/sharepoint/v3"

       StaticName="VersionLinkFilename" FromBaseType="TRUE">

  <FieldRefs>

    <FieldRef Name="FileLeafRef" />

    <FieldRef Name="FileRef" />

    <FieldRef Name="_UIVersion" />

  </FieldRefs>

  <DisplayPattern>

    <HTML><![CDATA[<a href=']]></HTML>

    <HttpVDir CurrentWeb="TRUE" ></HttpVDir><HTML><![CDATA[/_layouts/versionredir.aspx?FileRef=%2f]]></HTML><LookupColumn Name="FileRef" URLEncode="TRUE" /><HTML><![CDATA[&Version=]]></HTML><Column Name="_UIVersion" />

    <HTML><![CDATA['>]]></HTML>

    <LookupColumn Name="FileLeafRef" />

    <HTML><![CDATA[</a>]]></HTML>

  </DisplayPattern>

</Field>

The easiest way to add a computed filed to your document library is using code (or a custom tool), basically you can use the SPFieldCollection.AddFieldAsXml method (calling it on the Fields property of your SPList instance) providing the schema of the field in the first parameter.

And the code of the custom application page:

<%@ Page Language="C#" Debug="true" %>

<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Import Namespace="Microsoft.SharePoint" %>

<script runat="server">

    protected override void OnLoad(EventArgs e)

    {

        base.OnLoad(e);

 

        string fileRef = this.Request.QueryString["FileRef"];

        int version = int.Parse(this.Request.QueryString["Version"]);

 

        SPWeb web = SPContext.Current.Web;

        SPFile file = web.GetFile(fileRef);

        SPListItem item = file.Item;

 

        string redirUrl = null;

        if ((int)item["_UIVersion"] == version) redirUrl = web.Url.TrimEnd('/') + "/" + file.Url;

        else

        {

            foreach (SPListItemVersion v in item.Versions)

            {

                if ((int)v["_UIVersion"] == version)

                {

                    redirUrl = web.Url.TrimEnd('/') + "/_vti_history/" + version + "/" + file.Url;

                    break;

                }

            }

        }

        if (redirUrl != null)

        {

            Response.Redirect(redirUrl);

        }

    }

</script>

The page should be saved as “VersionRedir.aspx” in the LAYOUTS folder of your SharePoint hive.